JavaScript Event Delegation

Better event listeners


Event listeners on each button

    <div id="binding-every-element">
      <button>[1].addEventListener</button>
      <button>[2].addEventListener</button>
      <button>[3].addEventListener</button>
    </div>
    
    // Commonly seen in the wild
    // Grab the parent, loop through children, add event listeners to each.

    var binding_events_container = document.getElementById('binding-every-element');
    var binding_events_buttons = binding_events_container.getElementsByTagName('button');

    for ( var i = 0, len = binding_events_buttons.length; i < len; i++ ){

      var button = binding_events_buttons[i];
      
      button.addEventListener('click', function(event){
        its.a(event.target);
      });
                              
    }
    

Disadvangages:

  1. Slower to bind events
  2. If any new items are added:
    new_element.addEventListener('click', sameFunction);
    

Adding one event listener to the parent

Keep in mind - this isn't a button
    // Other elements are intentionally nested here.
    <div id="event-delegation">
      <button>Event Delegation Button 1</button>
      <button>Event Delegation Button 2</button>
      <button>Event Delegation Button 3</button>

      <div>Keep in mind - this isnt a button</div>
      <div style="border:1px solid red">
        <button>And this div has a button inside - and this button also has an event</button>
      </div>
    </div>
    
    // Event Delegation
    // The event propagates throughout the children elements
    // One event, all future elements.

    var event_delegation_container = document.getElementById('event-delegation');

    event_delegation_container.addEventListener('click', function(e){
      its.a(e.target);
    });
    

With Event Delegation, any element inside the container will receive the event.

We can also write conditional logic to capture specific elements with event.target.nodeName

    event_delegation_container.addEventListener('click', function(event){
      // Only button elements will do this
      if ( event.target.nodeName === 'BUTTON' ){
        its.a(event.target);
      }
    });
    

Organizing events with switch and data-attributes

    <div id="switch-container">
      <button data-navigation="home">Home</button>
      <button data-navigation="about">About</button>
      <button data-navigation="other">Other things</button>
      <input data-navigation="input" type="button" value="this is an input type=button - not a BUTTON"></input>
    </div>
    
    var switch_container = document.getElementById('switch-container');

    function myOrganizedEvents(event){
      // Get the data-attribute "data-navigation" value from the button clicked
      var navigation_data = event.target.getAttribute('data-navigation');
      
      // Switch is like a bunch of else if's
      switch(navigation_data) {

        case 'home':
          // Run home specific functions
          its.a('homeTemplate() - navigation_data = ' + navigation_data, false);
          break;

        case 'about':
          // Run about specific functions
          its.a('aboutTemplate() - navigation_data = ' + navigation_data, false);
          break;
          
        case 'other':
          // Run other specific functions
          its.a('otherTemplate() - navigation_data = ' + navigation_data, false);
          break;

        default:
          // A nice little message to self if nothing matches
          console.log('data-navigation action not found');
          break;
      }
      
    }

    // Event Delegation Binding events on parent container
    switch_container.addEventListener('click', function(event){
      // Check if the element is a button
      if ( event.target.nodeName === 'BUTTON' ){
        // Run my event organization distribution function
        myOrganizedEvents(event);
      }
    });
    

I like organizing events with data-attributes and distrubting them through a switch statement.

Cleaner than a conditional soup, and organized in one place which is nice.