Event delegation

Event delegation uses event bubbling to manage all events of a certain type by specifying only one event handler.


Scenario 1: Add click events to 100 buttons. What do I do?

<body>
    <div id="div1">
        <button>click 1</button>
        <button>click 2</button>
        <button>click 3</button>
        ...
        <button>click 99</button>
        <button>click 100</button>
    </div>
Copy the code

A: Listen for the ancestor of the 100 buttons and wait for the bubble to determine if target is one of the 100 buttons.

div1.addEventListener('click',(e) => { const t = e.target if(t.tagName.toLowerCase() === "button") Console. log(t.extContent +' button clicked ')})Copy the code

Scenario 2: How to listen for the click event of an element that does not currently exist?

<body**>**
    <div id="div1">
    
    </div>
    <script>
setTimeout(() => {
    const button = document.createElement('button')
    button.textContent = 'click 1'
    div1.appenChild(button)
},100)
    </script>
</body>
Copy the code

A: Listen for ancestors, and then click to see if it is the element I want to listen for.

div1.addEventListener('click',(e) => { const t = e.target if(t.tagName.tolowerCase() === "button"){ // Tag name of target element is smaller and written to match console.log('button was clicked ')}})Copy the code

As you can see from the above two scenarios, event delegation has two advantages: 1. Saving listening (memory). 2. You can listen on dynamic elements.

Based on the event delegate principle and the code example above, you can encapsulate the event delegate. Write a function on(‘click’, ‘#testDiv’, ‘li’, fn) based on the scene #testDiv>li that calls fn when the user clicks on the li element in #testDiv. Check whether target matches ‘li’.

Example Code 1

function on(eventType, element, selector, fn) { if(! (element instanceof Element)) { element = document.querySelector(element) } element.addEventListener(eventType, (e) => { const t = e.target if(t.matches(selector)){ fn(e) } }) }Copy the code

Example code 1, however, only addresses the case where the target element is exactly equal to the selector element, i.e., t.matches(selector)? . The above function cannot delegate events when it encounters the following situation.

Example Code 2

setTimeout(() => { const button = document.createElement('button') const span = document.createElement('span') span.textContent = "click 1" button.appendChild(span) div1.appendchild(button) },1000) on('click', '#div1', 'button', () => {console.log('button clicked ')})Copy the code

In example code 2, the selector element (button) does not match the target element (span), and the on function is not executed. So we need to rewrite the on function. According to the bubbling principle, starting with the target element, the target’s ancestor and selector elements are bubbled through in order to match, until propagated to the delegate element scope boundary, bubbling to #div1. When an ancestor element along the way matches the selector element, the fn function is passed to the matched ancestor element.

Reworked example code 1,onfunction

function on(eventType, element, selector, fn) { element.addEventListener(eventType, e => { let el = e.target while (! El.matches (selector)) {// Recursive bubble target's ancestor if (element === el) {// el = null break when matching selector element} el = el.parentNode } el && fn.call(el, e, el) // Pass in the matched ancestor element and target element to perform fn}) return element},Copy the code

$(‘# XXX ‘).on(‘click’, ‘li’, fn)


Other content

Question: Does JS support events? A: Yes and no. This section describes DOM events provided by browsers rather than JS functions. JS simply calls the addEventListener provided by the DOM. To enable JS to support events, you can write an event system in JS.


MDN Event reference