0

I'm really newbie in vanilla JS, so I'm a bit confused.

Let's say I have this html structure:

<div class="play-animation"></div>
<div class="play-animation"></div>
<div class="play-animation"></div>
<div class="play-animation"></div>
<div class="play-animation"></div>

I have this code to check if I click on one of those items:

document.addEventListener('click', function(item) {

  if(item.target.matches('.play-animation')) {

    console.log(item);
  }
})

And it doesn't console.log the element I've clicked. I understand why it doesn't console it, but I don't know how to rebuild code, so it'll console.log item I've clicked.

Thank you very much for your help in advance!

  • 1
    You should include the HTML as well..... – Mamun Nov 26 '18 at 12:34
  • Possible duplicate of [How to add a onclick event to an element using javascript](https://stackoverflow.com/questions/20018170/how-to-add-a-onclick-event-to-an-element-using-javascript) – Zohir Salak Nov 26 '18 at 12:37
  • @Mamun added! thank you! –  Nov 26 '18 at 12:37
  • @ZohirSalak Where did you find a reference for `.getElementsByClassName()`? – Andreas Nov 26 '18 at 12:40
  • Please add a [mcve] which shows the actual problem. – Andreas Nov 26 '18 at 12:40
  • You haven't provided a [mcve]. We don't know what `self` is, and the div elements have no content so aren't clickable. If I try to reproduce the problem and fill in the gaps with guesswork, the only reason it doesn't log the element is because you are logging the event object … but you know how to access the element as you did it on the previous like (it is `item.target`, not just `item`). So this question is either off-topic because the problem is a typo or off-topic because the MCVE is missing. – Quentin Nov 26 '18 at 12:43
  • 2
    Re edit: Now the only problem appears to be that you are logging `item` instead of `item.target`. – Quentin Nov 26 '18 at 12:45

2 Answers2

0

There are lot of solutions to bind event to elements. one with javascript and DOM events is like:

document.querySelectorAll('.play-animation').forEach(function (item) {
  item.addEventListener('click', function(){
      console.log(this);
  })
})
div{
  border:1px solid red;
  padding:4px;
}
<div class="play-animation">1</div>
<div class="play-animation">2</div>
<div class="play-animation">3</div>
<div class="play-animation">4</div>
<div class="play-animation">5</div>

UPDATE:
As Quentin mentioned in the comment and t-j-crowder answered in the next post, Also according to this article that describes the advantage of using event delegation, I add the following section to improve the answer:

document.querySelector('.container').addEventListener('click', function (event) {
    if (event.target.matches('.item')) {
        console.log('item clicked')
    } else {
        console.log('I am a', event.target.nodeName, ' not `item`');
    }
})

setTimeout(function () {
    var newItem = document.createElement('div');
    newItem.innerText = "I added 3 seconds later. but I have event too!";
    newItem.className = 'item';
    document.querySelector('.container').appendChild(newItem)
}, 3000)
.container .item,
.container .not-item {
    border: 1px solid red;
    padding: 5px;
}
<div class="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="not-item">4</div>
</div>

Well, in this above snippet you can see by adding an event on the parent of some elements, the children also will have that event too. And that event delegating is live, this means Later by adding some children to the parent, the new child will have that event too, and there is no need to bind the event to new ones.
This also is available in jQuery library by method: on like this:

$('.container').on('click','.item',function(){
    // item clicked, no matter when item is added to the container.
})

P.S:
Also browser compatibility of matches method in this link

Bagherani
  • 1,616
  • 1
  • 19
  • 32
0

target is the element that was clicked. With your quoted HTML, assuming there's some CSS that makes those divs not zero-height (as they will be without content), it happens that that will always be the element you want to check because there are no descendant elements in your divs, but from your question I suspect you have descendant elements you haven't shown, for instance:

<div class="play-animation">Some <span>styled</span> text</div>
<div class="play-animation"><img src="2.png"></div>

If the user clicks the span or img above, item.target in your code (item really should be event or e) will be the span or img, not the div.

To find out if the click passed through your div, you use a combination of Element#closest and Node#contains:

document.addEventListener('click', function(event) {
    var div = event.target.closest(".play-animation");
    if (div && this.contains(div)) {
        console.log("Click passed through .play-animation");
    } else {
        console.log("Click didn't pass through .play-animation");
    }
});
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<span>text in span but not in a .play-animation div</span>

Now, since you're hooking the event on document, you actually don't need the contains call:

document.addEventListener('click', function(event) {
    var div = event.target.closest(".play-animation");
    if (div) {
        console.log("Click passed through .play-animation");
    } else {
        console.log("Click didn't pass through .play-animation");
    }
});
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<div class="play-animation"><span>text in span</span></div>
<span>text in span but not in a .play-animation div</span>

You would, though, if you were hooking the event on anything but document (or possibly anything but document and document.body).


If, for some reason, you have to support obsolete browsers that don't have Element#closest, you can handle the loop doing the check yourself, which would also mean you don't need the contains call because you can fold that check in:

var element = event.target;
while (element) {
    if (element.matches(".play-animation")) {
        break;
    } else if (element === this) {
        element = null;
    }
}
if (element) {
    // `element` is a `.play-animation` element
}
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639