0

I have a card div in my application. When the user clicks this card element, it should fire a modal. However, I can't seem to attach the eventlistener for all elements inside the card element.

The card element looks like this:

<div class="card" data-id=${index}>
      <div class="card-img-container">
          <img class="card-img" src="${thumbnail}" alt="profile picture">
      </div>
      <div class="card-info-container">
          <h3 id="name" class="card-name cap">${firstname} ${lastname}</h3>
          <p class="card-text">${email}</p>
          <p class="card-text cap">${city}, ${state}</p>
      </div>
  </div>

How do I create an eventlistener that listen for all the elements inside the card element? Here's what my eventlistener looks like:

document.querySelector('.card').addEventListener('click', (e) => {
let dataId = parseInt(e.target.dataset.id);
let user = users[dataId];
showModal(user);

document.getElementById('modal-close-btn').addEventListener('click', () => {
  const modal = document.querySelector('.modal-container');
  document.body.removeChild(modal);
});

});

smac89
  • 39,374
  • 15
  • 132
  • 179
  • You shouldn't have to add a click handler to every element in the card. Click events bubble/propagate. It should be enough to put a click event handler on the card. – Taplar Jul 09 '20 at 18:06

3 Answers3

0

I think you are on track already - though your question is not very clear. You do not need to bind every element to a listener only capture the parent (in this case ".card") and determine which element was clicked just like you have done.

document.querySelector(".card").addEventlistener("click", function(el){ el.target; //was clicked });

If you really want to bind all child element in the card (not recommended), then document.querySelector(".card *").addEventListener("click", function(){ });

If this is not what you need then maybe I do not understand the question

Kjut
  • 56
  • 4
0

IF you need to set the event listener to several elements with the same class, you could use a for loop to loop though each element.. Attached is your code with a for loop around it.

//Create a for loop and loop through all elements that coorespond with (document.querySelectorAll('.card'))

    //Sidenote: querySelectorAll() returns an array of all matching elements, so you can simply add an event listsner to each element in that array by it's index or by adding "[i]" 
    for(let i =0; i < document.querySelectorAll('.card').length; i++){

        document.querySelectorAll('.card')[i].addEventListener('click', (e) => {
        let dataId = parseInt(e.target.dataset.id);
        let user = users[dataId];
        showModal(user);

            document.getElementById('modal-close-btn').addEventListener('click', () => {
              const modal = document.querySelector('.modal-container');
              document.body.removeChild(modal);
            });
        });
    }


    //This should attach your "click" event listener to any elements with the class of "card"

This sets the event listener to all "cards" but you can apply the same method/for loop for any element. Hope this helps

StrayAnt
  • 369
  • 2
  • 7
0

There is no reason to add an event listener for every element in the card. Instead bind an event listener to the .card element. Then use event.currentTarget to get your element.

Remember, target is the element you clicked and currentTarget is the current element in the bubbling process (the element that the event is bound to).

In your example e.target.dataset.id will most likely return undefined because you click on an element inside the .card div. parseInt(undefined) will return NaN and users[NaN] will most likely also return undefined. This means most of the time you will be calling showModal(undefined) unless you click on the card padding (if there is any).

Using currentTarget should resolve the issue.

document.querySelector(".card").addEventListener("click", event => {
  console.log("target:", event.target);
  console.log("currentTarget:", event.currentTarget);
  // this is how you get your ".card" element ^
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

<div class="card" data-id=${index}>
    <div class="card-img-container">
        <img class="card-img" src="${thumbnail}" alt="profile picture">
    </div>
    <div class="card-info-container">
        <h3 id="name" class="card-name cap">${firstname} ${lastname}</h3>
        <p class="card-text">${email}</p>
        <p class="card-text cap">${city}, ${state}</p>
    </div>
</div>
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
  • Thank you very much! That helped solve the problem. I guess I don't quite get the difference between e.target / e.currentTarget. Although a bit clearer now, thanks! – Fredrik Johansson Jul 10 '20 at 12:13
  • @FredrikJohansson Check out the [`target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) and [`currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget) documentation for a better explanation. If you are new to events in general I recommend checking out the [MDN Introduction to events](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events) guide. – 3limin4t0r Jul 10 '20 at 12:26
  • There is also a separate SO question that about this: [What is the exact difference between currentTarget property and target property in javascript](https://stackoverflow.com/q/10086427/3982562) – 3limin4t0r Jul 10 '20 at 12:35