15

I'm doing a small application just to learn ES6 stuff and I've found a problem with addEventListener and parent/child events.

I have a menu consisting on several divs that have an image (an avatar) and some text. Each div has an data-id attribute to get the clicked elements id and I have put an addEventListener on each row:

<div class="row" data-id="1">
  <img src="avatar" />
  Lorem ipsum
</div>
...

And the js:

const rows = document.querySelectorAll('.row');
rows.forEach(row => row.addEventListener('click',selectRow));

function selectRow(e){
  var row = e.target;
  alert(row.dataset.id);
}

The problem comes when I click on the avatar. It fires the click event, but the target is the img element, not the div, so I can't get the data-id attribute.

I have tried many approaches (like this one) but it prevents firing the event when clicking on the avatar, so not on a solution as the avatar is part of the row and it may get clicked :S

I have set a jsfiddle to show my problem. Any help would be appreciated :)

https://jsfiddle.net/igorosabel/o64b404y/1/

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Iñigo Gorosabel
  • 502
  • 1
  • 4
  • 22
  • 1
    Rather than jsFiddle, please use Stack Snippets (the `[<>]` button) here on site, so that **all** the code and markup and such is contained **in** the question. (Your question has enough code anyway, but hey, if you've done the runnable example, best to include it.) – T.J. Crowder Mar 07 '17 at 09:07

1 Answers1

18

It fires the click event, but the target is the img element, not the div, so I can't get the data-id attribute.

Correct. target is the actual target of the event, regardless which element you hooked the event on, which may be different from the actual target because of bubbling (in your case, click bubbles from the img to the div).

You can get the element you hooked the event on via this (provided you let the event system set this for the handler) or e.currentTarget.

Your fiddle as a snippet with that change:

const rows = document.querySelectorAll('.row');
rows.forEach(row => row.addEventListener('click',showId));

function showId(e){
  console.log("Using this:", this.dataset.id);
  console.log("Using e.currentTarget:", e.currentTarget.dataset.id);
}
.row{
  border: 1px solid #000;
  margin-bottom: 10px;
  padding: 10px;
}
<div class="row" data-id="1">
  <img src="https://placehold.it/100x100" />
  Lorem ipsum
</div>
<div class="row" data-id="2">
  <img src="https://placehold.it/100x100" />
  Lorem ipsum
</div>
<div class="row" data-id="3">
  <img src="https://placehold.it/100x100" />
  Lorem ipsum
</div>
evolutionxbox
  • 3,932
  • 6
  • 34
  • 51
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    @IñigoGorosabel: :-) So long as you don't use an arrow function or bound function for the handler, yeah (since arrows close over `this` instead of having their own, and bound functions ignore the `this` they're called with). The event handling system uses `e.currentTarget` as `this` when calling your handler. – T.J. Crowder Mar 07 '17 at 09:35
  • for arrow function, we have to use selector to get the `this`. in your function, it will be `row` – WBGUY001 Sep 18 '21 at 23:30
  • @WBGUY001 I don't understand what you're trying to say there...? – T.J. Crowder Sep 19 '21 at 14:27
  • for arrow function, `rows.forEach(row => row.addEventListener('click',() => { console.log(row) }));` – WBGUY001 Sep 20 '21 at 12:32
  • @WBGUY001 - Yes, they *could* do that as well, but there's no particular reason to, and it creates a separate function for each element. – T.J. Crowder Sep 20 '21 at 12:49