skip to Main Content

I’m working on a project where I need to make a cinema website. Right now I’m working on the movie specific page. An example of how part of this looks can be found in the snippet below. The trouble I have is with the eventlistener highlighted with the ///////////// lines in the js file.

The movie tickets div has the following structure:

div.movie-tickets
├── span.tickets-title
├── div.tickets__day
│   ├── div.tickets__day-name
│   ├── div.tickets__showtime
│   │   ├── span (time)
│   │   ├── span (seats left)
│   │   └── span (tickets in cart)
│   └── ...
└── ...

when the user selects a ticket, I want to change seats left (-1) and tickets in cart (+1) text. So in the eventlistener, I’d like to edit the relevant div.tickets_showtime element.

My plan was to use e.target for this, but this digs one layer too deep. How do I access the div.tickets_showtime element in the eventlistener upon clicking. No matter where the user clicks on a movie-showing?

Thanks in advance!!

class Movie {
    constructor(title, image, description, duration, release, age, schedule, seats){
        this.title = title;
        this.image = image;
        this.description = description;
        this.duration = duration;
        this.release = release;
        this.age = age;
        this.schedule = schedule; //dictionary met alle tijden per dag.
        this.seats = seats;
    }
}

var JW4 = new Movie (
  "John Wick 4 ",
  "https://media.pathe.nl/nocropthumb/180x254/gfx_content//api/filmdepot/v1/movie/download/34482/34482_157906_ps_sd-high.jpg",
  "nDe prijs op John Wick z’n hoofd wordt steeds hoger en zijn gevecht tegen het internationale moordenaarsgilde The High Table ontwikkelt zich tot een wereldwijde strijd. Om zijn vrijheid terug te krijgen moet hij het opnemen tegen een nieuwe vijand met machtige allianties over de hele wereld en krachten die oude vrienden in vijanden veranderen.",
  "169 minuten",
  "Release: 23-03-2023",
  "/assets/img/icons/kijkwijzer/kijkwijzer-16-gray-32.png",
  "{"Vandaag":[["10:45-13:49",110],["13:45-16:49",125],["16:20-19:24",201],["17:50-20:54",35],["20:00-23:04",187],["22:00-01:04",189]],"Zondag":[["10:00-13:04",163],["14:45-17:49",13],["18:10-21:14",91],["20:45-23:49",135]],"Maandag":[["11:30-14:34",146],["12:10-15:14",97],["15:00-18:04",25],["18:00-21:04",196],["20:30-23:34",100]],"Dinsdag":[["11:30-14:34",120],["15:00-18:04",190],["18:00-21:04",81],["20:40-23:44",73]],"Woensdag":[["11:50-14:54",92],["17:50-20:54",167],["20:15-23:19",27]],"Donderdag":[["11:30-14:34",89],["15:00-18:04",30],["18:00-21:04",20],["20:40-23:44",39]],"Vrijdag":[["12:10-15:14",200],["15:40-18:44",148],["19:20-22:24",75],["21:20-00:24",123]],"Zaterdag":[["12:10-15:14",181],["16:30-19:34",12],["19:20-22:24",205],["21:30-00:34",156]]}",
  null
)

function loadMoviePage() {
    populate(JW4)
}

function populate(movie) {
    var content = document.querySelector(".content");
    addMovieInfo(content, movie);
}

function addMovieInfo(location, movie) {

    var tickets = document.createElement("div");
      tickets.classList.add("movie-tickets");

        var ticketstitle = document.createElement("span");
        ticketstitle.classList.add("tickets-title");
        ticketstitle.innerText = 'TICKETS';

        tickets.appendChild(ticketstitle);

        var schedule = JSON.parse(movie.schedule);

        for (const prop in schedule) {
            var day = prop.toString();
            var movies = schedule[prop];

            var daycontainer = document.createElement('div');
            daycontainer.classList.add('tickets__day')

                var dayname = document.createElement('div');
                dayname.classList.add('tickets__day-name');
                dayname.innerText = day;

                daycontainer.appendChild(dayname);

                for (const film of movies) {
                    var moviebutton = document.createElement('div');
                    moviebutton.classList.add('tickets__showtime');
                    
                    var movietime = document.createElement('span');
                    movietime.innerText = film[0];

                    var seats = document.createElement('span');
                    seats.innerText = film[1].toString() + ' left';

                    var incart = document.createElement('span');
                    incart.innerText = '0 in cart';

                    moviebutton.appendChild(incart);
                    moviebutton.insertBefore(seats, incart);
                    moviebutton.insertBefore(movietime, seats);

                    daycontainer.appendChild(moviebutton);
                }
            tickets.appendChild(daycontainer);
        }

        var addtocart = document.createElement("span");
        addtocart.classList.add("tickets-in-cart");
        addtocart.innerText = 'ADD TO CART';

        tickets.appendChild(addtocart);
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
    tickets.addEventListener('click', function(e) {
        console.log(e.target)
    })
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

      location.appendChild(tickets);
}

window.addEventListener("load", loadMoviePage);
body {
    font-family: 'Teko', sans-serif;
    margin: 50px 20%;
    background-image: linear-gradient(0deg, rgba(0,0,0,1) 30%, rgba(253,126,126,0) 90%),
    url(https://images.unsplash.com/photo-1574267432553-4b4628081c31?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTF8fG1vdmllJTIwdGhlYXRlcnxlbnwwfHwwfHw%3D&w=1000&q=80);
    background-color: black;
    background-repeat: no-repeat;
    background-size: 100vw 100vh;
    color:rgb(246, 199, 199);
}

.movie-tickets {
    background:rgb(61, 0, 0);
    margin: 20px 0;
    display: flex;
    flex-direction: column;
}

.tickets-title, .tickets__day-name, .tickets-in-cart {
    width: 100%;
    text-align: center;
    font-size: 1.7em;
}

.tickets-in-cart {
    background-color: rgb(90, 2, 2);
}

.tickets__showtime {
    font-size: 1.3em;
    display:flex;
    justify-content: space-around;
    padding: 0 20%;
    transition: all 100ms ease-in-out;
}

.tickets__showtime:hover {
    background-color: rgb(90, 2, 2);
    transform: scale(1.05);
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Movie Page</title>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Open+Sans&family=Teko:wght@300&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="/css/general.css">
        <link rel="stylesheet" href="/css/movie.css">
    </head>
    <body class="moviebody">
        <article class="content">
        </article>

        <script src="/js/movie.js" type="module"></script> 
        <script src="/js/general.js" type="module"></script> 
    </body>
    
</html>

2

Answers


  1. New way – Element.closest – gets the closest ancestor element that matches a selector – better because it’s less fragile to html structure changes

    e.target.closest(".tickets__showtime")
    

    Old way – to get the parent node of some element, use

    e.target.parentNode
    
    Login or Signup to reply.
  2. this is a classic case of event delegation
    the event code should be:

    tickets.addEventListener('click', evt =>
      {
      let moviebutton = evt.target.closest('.tickets__showtime') || tickets; // if null set tickets
    
      if (moviebutton.matches('.tickets__showtime')) // check for correct line
        {
        let 
          cLeft      = moviebutton.querySelector('span:nth-of-type(2)')
        , inCart     = moviebutton.querySelector('span:nth-of-type(3)')
        , cLeft_val  = Number( cLeft.textContent.replace(/D/g, ''))
        , inCart_val = Number( inCart.textContent.replace(/D/g, ''))
          ;
        if (cLeft_val > 0 )
          {
          cLeft.textContent  = `${--cLeft_val} left`;  
          inCart.textContent = `${++inCart_val} in cart`;
          }
        }
      })
    

    in context :

    class Movie 
      {
      constructor(title, image, description, duration, release, age, schedule, seats) 
        {
        this.title       = title;
        this.image       = image;
        this.description = description;
        this.duration    = duration;
        this.release     = release;
        this.age         = age;
        this.schedule    = schedule; // dictionary met alle tijden per dag.
        this.seats       = seats;
      } }
    
    var JW4 = new Movie(
      "John Wick 4 ",
      "https://media.pathe.nl/nocropthumb/180x254/gfx_content//api/filmdepot/v1/movie/download/34482/34482_157906_ps_sd-high.jpg",
      "nDe prijs op John Wick z’n hoofd wordt steeds hoger en zijn gevecht tegen het internationale moordenaarsgilde The High Table ontwikkelt zich tot een wereldwijde strijd. Om zijn vrijheid terug te krijgen moet hij het opnemen tegen een nieuwe vijand met machtige allianties over de hele wereld en krachten die oude vrienden in vijanden veranderen.",
      "169 minuten",
      "Release: 23-03-2023",
      "/assets/img/icons/kijkwijzer/kijkwijzer-16-gray-32.png",
      "{"Vandaag":[["10:45-13:49",110],["13:45-16:49",125],["16:20-19:24",201],["17:50-20:54",35],["20:00-23:04",187],["22:00-01:04",189]],"Zondag":[["10:00-13:04",163],["14:45-17:49",13],["18:10-21:14",91],["20:45-23:49",135]],"Maandag":[["11:30-14:34",146],["12:10-15:14",97],["15:00-18:04",25],["18:00-21:04",196],["20:30-23:34",100]],"Dinsdag":[["11:30-14:34",120],["15:00-18:04",190],["18:00-21:04",81],["20:40-23:44",73]],"Woensdag":[["11:50-14:54",92],["17:50-20:54",167],["20:15-23:19",27]],"Donderdag":[["11:30-14:34",89],["15:00-18:04",30],["18:00-21:04",20],["20:40-23:44",39]],"Vrijdag":[["12:10-15:14",200],["15:40-18:44",148],["19:20-22:24",75],["21:20-00:24",123]],"Zaterdag":[["12:10-15:14",181],["16:30-19:34",12],["19:20-22:24",205],["21:30-00:34",156]]}",
      null
    )
    
    function loadMoviePage() {
      populate(JW4)
    }
    
    function populate(movie) {
      var content = document.querySelector(".content");
      addMovieInfo(content, movie);
    }
    
    function addMovieInfo(location, movie)
      {
      let tickets       = document.createElement('div');
      tickets.className = 'movie-tickets';
    
      let ticketstitle         = document.createElement('span');
      ticketstitle.className   = 'tickets-title';
      ticketstitle.textContent = 'TICKETS';
    
      tickets.appendChild(ticketstitle);
    
      let schedule = JSON.parse(movie.schedule);
    
      for (const prop in schedule) 
        {
        let daycontainer       = document.createElement('div');
        daycontainer.className = 'tickets__day';
        daycontainer.innerHTML = `
          <div class="tickets__day-name">${ prop }</div>`;
    
        for (const [dte,count] of schedule[prop])
          {
          let moviebutton       = document.createElement('div');
          moviebutton.className = 'tickets__showtime';
          moviebutton.innerHTML = ` 
            <span>${ dte }</span>
            <span>${ count } left</span>
            <span>0 in cart</span>`;
          daycontainer.appendChild(moviebutton);
          }
        tickets.appendChild(daycontainer);
        }
    
      let addtocart         = document.createElement('span');
      addtocart.className   = 'tickets-in-cart';
      addtocart.textContent = 'ADD TO CART';
    
      tickets.appendChild(addtocart);
      location.appendChild(tickets);
    
    tickets.addEventListener('click', evt =>
      {
      let moviebutton = evt.target.closest('.tickets__showtime') || tickets; // if null set tickets
    
      if (moviebutton.matches('.tickets__showtime')) // check for correct line
        {
        let 
          cLeft      = moviebutton.querySelector('span:nth-of-type(2)')
        , inCart     = moviebutton.querySelector('span:nth-of-type(3)')
        , cLeft_val  = Number( cLeft.textContent.replace(/D/g, ''))
        , inCart_val = Number( inCart.textContent.replace(/D/g, ''))
          ;
        if (cLeft_val > 0 )
          {
          cLeft.textContent  = `${--cLeft_val} left`;  
          inCart.textContent = `${++inCart_val} in cart`;
          }
        }
      })
      }
    
    window.addEventListener('load', loadMoviePage);
    body {
      font-family       : 'Teko', sans-serif;
      margin            : 50px 20%;
      background-image  : linear-gradient(0deg, rgba(0,0,0,1) 30%, rgba(253,126,126,0) 90%)
      , url(https://images.unsplash.com/photo-1574267432553-4b4628081c31?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTF8fG1vdmllJTIwdGhlYXRlcnxlbnwwfHwwfHw%3D&w=1000&q=80);
      background-color  : black;
      background-repeat : no-repeat;
      background-size   : 100vw 100vh;
      color             : rgb(246, 199, 199);
      }
    .movie-tickets {
      background     : rgb(61, 0, 0);
      margin         : 20px 0;
      display        : flex;
      flex-direction : column;
      }
    .tickets-title, .tickets__day-name, .tickets-in-cart {
      width      : 100%;
      text-align : center;
      font-size  : 1.7em;
      }
    .tickets-in-cart {
      background-color : rgb(90, 2, 2);
      }
    .tickets__showtime {
      font-size       : 1.3em;
      display         : flex;
      justify-content : space-around;
      padding         : 0 20%;
      transition      : all 100ms ease-in-out;
      cursor          : pointer;                 /* added */
      }
    .tickets__showtime:hover {
      background-color : rgb(90, 2, 2);
      transform        : scale(1.05);
      }
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Open+Sans&family=Teko:wght@300&display=swap" rel="stylesheet">
    
    <article class="content"></article>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search