Friday, December 06, 2019

Event delegation in JavaScript

In JavaScript we use event delegation for two main reasons:
1) to be able to process events coming from dynamically added page elements.
2) to achieve better application performance by listening to only one main element instead of having to add and remove event listeners each time a new element is dynamically added to the DOM.
More intriguing JavaScript aspects you can discover inside the JavaScript for beginners - learn by doing course.

 


In case of event delegation, when event occurs, we can filter out on which of the event generated source elements we would like to respond to using the event's target property: event.target
Here are two examples on how to use the event target:
// to filter out the processing of events coming from elements having different classes:
if (!event.target.classList.contains('my_class')) return;
// or to do filtering based on particular tag:
if (event.target.tagName == 'INPUT') {
// do our processing only if the source event came from an input tag
}

Example: Here we create an html with a simple ul/li list and add new LI button:
<button id="add">Add new LI</button>
<ul class="characters">
    <li>
        <input type="checkbox" data-index="0" id="child0">
        <label for="child0">Child 0</label>
    </li>
    <li>
        <input type="checkbox" data-index="1" id="child1">
        <label for="child1">Child 1</label>
    </li>
    <li>
        <input type="checkbox" data-index="2" id="child2">
        <label for="child2">Child 2 </label>
    </li>
</ul>
<script src="event_delegate.js"></script>

And here is our JavaScript code which adds new LI elements dynamically:
document.querySelector('#add').addEventListener('click', () => {
 // we create a new li element
let li = document.createElement('li');
// we get the last data-index attribute withing ul>li
    let dataId = characterList
        .lastElementChild // we use ElementChild instead of Child, because it ignores text and comment nodes
        .firstElementChild
        .getAttribute('data-index');
// increase the last id with 1
    ++dataId;
// construct the ne LI element with increased dataId
    li.innerHTML = `
    <input
    type="checkbox"
    data-index="${dataId}"
    id="child${dataId}">
    <label for="child${dataId}">Child ${dataId}</label>
    `;
// append the new LI eleemnt to the UL
    characterList.append(li);
});

// this is our little debug function showing up the event and where it is coming from
function toggle(event) {
    console.log('event: ' + event); //the event
    console.log('target: ' + event.target); //where occured the event
    console.log('currentTarget: ' + event.currentTarget); //attached parent element
    console.log('toggled element with id: ' + event.target.id);
}

// the actual parent element, where the event delegation is happening
const characterList = document.querySelector('.characters');
// here, instead of having multiple LI attached event listeners, we just attach 1 event listener to the UL list
characterList.addEventListener('click', toggle);

// Bonus: here is how to query specific element using its data-index attribute:
console.log(document.querySelector('input[data-index="1"]'));

Congratulations!

Resources:

JavaScript for beginners - learn by doing

Subscribe To My Channel for updates