javascriptaddeventlistenerevent-bubblingevent-capturing

Detect intermediate node with javascript bubbling/capturing


I need to capture an anchor node with an image inside using event delegation.

document.addEventListener(
  'click',
  function(event) {
    console.log(event.target);
    return true;
  },
  false
);
<a href="#" class="link">
  <img src="http://placehold.it/100x100" alt="">
</a>

event.target is always img.

How can check if the click was made on a node with class .link?

UPDATE: to be clear here is an example with jQuery

When I use jQuery.on() there is a node in this property in callback fucntion, not img node. With pure JS I can determine initial target only.


Solution

  • You can check if an element has a class by calling:

    element.classList.contains('link');
    

    What you want right now is to do something when an <img> within an <a class="link"> is clicked. To determine if the clicked <img> has a parent <a class="link"> we must traverse up its parent tree and check.

    This is very similar behavior as the jQuery example you have, i.e.

    $('body').on('click', '.link', callback)
    

    except the jQuery matches a whole query, not just a class.

    Here's how you could do this:

    // function to determine if the element has the link class
    const hasLinkClass = element => element
      ? element.classList && element.classList.contains('link')
      : false;
    
    // function that accepts an event handler and returns
    // a wrapper function arround it.
    // The wrapper is called it if the passed in event 
    // object contains as its target an <img> with a parent
    // with .link class
    function filterImagesWithinLinks(handler) {
      return function(event) {
        let elem = event.target;
    
        // ignore clicks that are not on an image (it might be better to be more specific here)
        if (elem.tagName === 'IMG') {
        
          // filter until we find the parent with .link class
          while (elem && !hasLinkClass(elem)) {
            elem = elem.parentNode;
          }
    
          // if a parent with .link class was found, 
          // call the original handler and set its `this`
          // to the parent.
          if (elem) {
            handler.call(elem, event);
          }
        }
      };
    }
    
    // function handler that fires when 
    // an <img> that has a parent with 
    // class 'link' was clicked
    function handler(event) {
      console.log('target : ', event.target);
      console.log('this   : ', this);
    }
    
    document.addEventListener(
      'click',
      filterImagesWithinLinks(handler),
      false
    );
    a.link {
      display: block;
      padding: 10px;
      background: #018bbc;
    }
    
    .middle {
      background: salmon;
      padding: 10px;
      text-align: center;
    }
    <a href="#" class="link">
      <p class="middle">
        <img src="http://placehold.it/100x100" alt="" />
      </p>
    </a>