DOM Event Delegation

DOM event delegation is simply letting a parent element look after his/her kids, leaving the kids alone to play. So instead of adding an event to each of the children, we add an event to the parent element. Adding event listeners willy-nilly pollutes the DOM, is not easy to manage i.e. you can lose track of removing event listeners etc.

Lets create a delegate function. It takes a DOM element (or string to use to select a DOM element), an event type (in our case a "click"), a selector (the element we are delegating), and a callback.

window.delegate = function (el, type, selector, callback) {
  el = typeof el === 'string' ? document.querySelector(el) : el;
  if (!el) {
    return;
  }
  el.addEventListener(type, function (event) {
    var node = event.target;
    while (node && node !== el) {
      if (node.matches(selector)) {
        callback.call(node, event);
      }
      node = node.parentNode;
    }
  });
};

So with the above we can create collapsible panes - toggling content:

(function () {
  var collapsibles = document.querySelectorAll('.collapsible');
  var handler = function (e) {
    var node = e.target;
    while (node && node !== e) {
      if (node.matches('.collapsible-item')) {
        node.classList.toggle('open');
        break;
      }
      node = node.parentNode;
    }
  };

//for all collapsible divs, add toggle to heading element for (var i = 0; i < collapsibles.length; i++) { window.delegate(collapsibles[i], 'click', '.collapsible-heading', handler); }

}());

And create some tabs:

(function () {
  var pane;
  var tabs = document.querySelectorAll('.tabs');
  var handler = function (e) {
    var children;
    var panes;
    var parent = e.target.parentNode;
    while (parent) {
      if (parent.matches('[role="tablist"]')) {
        children = parent.children;
        for (var i = 0; i < children.length; i++) {
          children[i].children[0].setAttribute('aria-selected', false);
        }
        e.target.setAttribute('aria-selected', true);
        pane = document.getElementById(e.target.getAttribute('aria-controls'));
        panes = pane.parentNode.children;
        for (var i = 0; i < panes.length; i++) {
          panes[i].setAttribute('aria-hidden', true);
        }
        pane.setAttribute('aria-hidden', false);
        break;
      }
      parent = parent.parentNode;
    }
    e.preventDefault();
    e.stopPropagation();
  };

//for every tab element for (var i = 0; i < tabs.length; i++) { window.delegate(tabs[i], 'click', '[role="tab"]', handler); }

}());

References