DEV Community

JS Bits Bill
JS Bits Bill

Posted on • Updated on

Event Delegation with Vanilla JS

One of the most powerful and convenient techniques in jQuery is event delegation - a way in which you can add one event handler to a parent element instead of attaching multiple handlers to many child elements.

So here's how to do it in vanilla!

<ul>
  <li>Sphynx</li>
  <li>Maine Coon</li>
  <li>Bristish Shorthair</li>
</ul>
Enter fullscreen mode Exit fullscreen mode
const ul = document.querySelector('ul');
ul.addEventListener('click', e => {
  const { target } = e;
  if (target.matches('li')) {
    callback(); // If target is an li, run callback
  }
});
Enter fullscreen mode Exit fullscreen mode

Here we attach just one event listener to the parent ul element, but inside our handler we check if the event's target matches an li. If so, we run our callback function.

This is all well and good for simple HTML lists, but what if the list items contain children of their own?

<ul>
  <li>
    <span>Blue</span> Sphynx
  </li>
  <li>
    Maine Coon
    <span>Rarer breed</span>
  </li>
  <li>
    <span>Fiesty</span> Bristish Shorthair
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

With this more complex HTML tree, the event's target may not be the <li> element. Any one of the <li>'s child spans may be clicked and would register as the target. To handle this, we could either add some extra logic to check for the next closest element up the tree OR we can simply add a CSS pointer-events: none; rule that will force the spans to never be the target of pointer events:

span {
  pointer-events: none;
}
Enter fullscreen mode Exit fullscreen mode

With that rule in place, even if the actual clicked element is a <span> within the <li>, the span is now considered a part of the <li>'s content and the event's target is considered to be the <li>, thus registering our callback with no fuss, no muss! 😃


Check out more #JSBits at my blog, jsbits-yo.com. Or follow me on Twitter.

Top comments (1)

Collapse
 
frontendplace profile image
Ron Jonk
const trackedElement = target.closest('li'); // then no need for css rules.
if(trackedElement ){ 
    cb()
}
Enter fullscreen mode Exit fullscreen mode