2

I've got a people search script that displays suggestions as you type. The structure for the results is loosely this:

<div  id="peopleResults">
  <div class="resultHolder">
    <div data-userid="ABC123" class="person">
        <div class="name">Last, First</div>
        <div class="title">Job Title</div>
        <a class="email" href="mailto:person@company.com">person@company.com</a><span class="phone">12345</span>
    </div>
    <!-- more people... -->
  </div>
</div>

Since the list of people gets altered as you type, up until now I was using JQuery's live() function to automatically bind click events to all the .person elements. This is deprecated and bad practice, so I'm trying to update the code.

I see that I could use something like:

$('#peopleResults').on('click', '.person', function(){
    // handle the click
})

But I want to understand a bit more about how to handle this in vanilla javascript. I THOUGHT that when clicking on the child of an item with a click event, the event would "bubble" up through the elements, and I could catch it when it hit the .person element. Excuse the sloppy code, but something like:

document.getElementById('peopleResults').addEventListener('click', function(e){
    if(e.target.classList.contains('person')) {
        // handle click 
    }
});

I've done similar things in the past, but usually with links. (.person can't be a link in this case because it contains an email link.)

So in this case it doesn't work. If I click on .name within .person, the target is .name.

This seems like something fundamental that just isn't clicking in my brain. What's the typical way to handle this in JavaScript?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
rdoyle720
  • 2,940
  • 3
  • 17
  • 19
  • possible duplicate of [Javascript click event listener on class](http://stackoverflow.com/questions/19655189/javascript-click-event-listener-on-class) – Jonast92 Dec 03 '14 at 15:24
  • @Jonast92 I don't see the connection between the two questions. – iMoses Dec 03 '14 at 16:15

2 Answers2

6

You can set pointer-events to none for all children of the desired targets:

.person > * {
  pointer-events: none;
}

This way event.target will be a .person element instead of one of its descendants.

document.getElementById('peopleResults').addEventListener('click', function(e){
  if(e.target.classList.contains('person')) {
    alert('Person clicked');
  }
});
.person > * {
  pointer-events: none;
}
<div  id="peopleResults">
  <div class="resultHolder">
    <div data-userid="ABC123" class="person">
      <div class="name">Last, First</div>
      <div class="title">Job Title</div>
      <a class="email" href="mailto:person@company.com">person@company.com</a><span class="phone">12345</span>
    </div>
    <!-- more people... -->
  </div>
</div>

Note that this approach will break the functionality of interactive elements (such as links) inside .person.

Oriol
  • 274,082
  • 63
  • 437
  • 513
3

When using jQuery events you are given two properties which you should be familiar with:

  1. target
  2. currentTarget

when using event delegation (meaning catching events on a parent element) target will be the element which initially triggered the event while currentTarget will be the element which catches the event.

Consider this code example:

$('#peopleResults').on('click', '.person', function(e) {
    if ($(e.currentTarget).hasClass('person')) console.log('yay!');
});

P.S. - when not using event delegation both properties should point to the same element.

For more info about currentTarget.

iMoses
  • 4,338
  • 1
  • 24
  • 39