The event

The interaction between JavaScript and HTML is achieved through events. What is an event? Events are specific moments of interaction that occur in a document or browser window. You can use listeners or handlers to schedule events so that the appropriate code executes when the events occur. This model, known as observer mode in traditional software engineering, supports a loose coupling between the behavior of the page (JavaScript code) and the look and feel of the page (HTML and CSS code). Events first appeared in IE3 and Netscape Navigator 2 as a way to share the load of server computing.

Dom0-level event handler

The traditional way to specify event handlers in JavaScript is to assign a function to an event handler property. This method of copying event handlers appeared in the fourth generation of Web browsers, and is still supported by all modern browsers today. One reason is simplicity, and the other is the cross-browser advantage. The usage is as follows:

// Add event handlers directly to the HTML code
<button id="btn" type="button" onclick="print()"></button>

const btn = document.getElementById('btn');
// Add an event handler
btn.onclick = print;
function print() {
    console.log('click');
}

// Remove the event handler
btn.onclick = null;
Copy the code

Dom2-level event handler

Dom2-level events define two methods for handling the action of specifying and removing event handlers: addEventListener() and removeEventListener(). All DOM nodes contain these two methods, and they each take three arguments: the name of the event to handle, the function that acts as the event handler, and a Boolean value. This last Boolean parameter, if true, calls the event handler during the capture phase. If false, the event handler is invoked during the bubbling phase. False by default. Use as follows

const btn = document.getElementById('btn');
// Add an event handler
btn.addEventListener('click', print, false);
// Remove the event handler
btn.removeEventListener('click', print, false);
function print() {
    console.log('click');
}
Copy the code

IE event handler

IE implements two methods similar to those in DOM: attachEvent() and detachEvent(). Both methods take the same two arguments: the event handler name and the event handler function. Because IE8 and earlier only support event bubbling, event handlers added via attachEvent() are added to the bubbling phase. The use method is as follows:

var btn = document.getElementById('btn');
// Add an event handler
btn.attachEvent('onclick', print);
// Remove the event handler
btn.detachEvent('onclick', print);
function print() {
    console.log('click');
}
Copy the code

Flow of events

As browsers moved into the fourth generation (IE4 and Netscape Communicator4), the browser development team faced an interesting question: Which part of the page would have a particular event? To understand what this question is asking, imagine a set of concentric circles drawn on a piece of paper. If you place your finger at the center of the circle, your finger will not point at one circle, but at all the circles on the paper. The browser teams at both companies are looking at browser events in the same way. If you click on a button, they all assume that the click happened on more than just the button. In other words, when you click the button, you also click the container element of the button, or even the entire page. The event flow describes the order in which events are received from the page. But it’s interesting that the IE and Netscape teams came up with the opposite concept of flow of events. IE’s event flow is an event bubble flow, while Netscape Communicator’s event flow is an event capture flow. Combined with MDN official schematic diagram for easy understanding.

As can be seen from the figure above, the event flow specified by DOM2-level event includes three stages, which are:

  1. Capture phase: Event objects are propagated from the Window to the target’s parent through the target’s ancestor. This phase is also known as the capture phase.
  2. Target stage: The event object reaches the event target of the event object. This phase is also known as the at target phase. If the event type indicates that the event is not bubbling, the event object will be stopped when this phase is complete.
  3. Bubbling phase: Event objects propagate through the target’s ancestors in reverse order, starting at the target’s parent and ending at the Window. This stage is also known as the bubbling stage.

Event delegation

Because event handlers provide the interaction capabilities of modern Web applications, many developers add a large number of event handlers indiscriminately to pages. Imagine if a list contains hundreds or thousands of list items and we add an event handler to each list item. This can lead to an “event handler overload” problem. For such problems, the solution is event delegation. Event delegates take advantage of event bubbling by specifying only one event handler to manage all events of a certain type. Here’s an example:

<ul id="list">
    <li class="item" data-id="0">List item 1</li><! -... --><li class="item" data-id="99">List item 100</li>
</ul>

// Traditional implementation
const list = document.querySelectorAll('.item');
// Loops to add event handlers for each list item
for (const item of list) {
    item.addEventListener('click'.function (e) {
        console.log(this.dataset.id);
    });
}

// Use event delegates
const ul = document.querySelector('#list');
// Add the event handler to the parent element
ul.addEventListener('click'.function (e) {
    if(e.target.tagName === 'LI') {console.log(e.target.dataset.id); }});Copy the code

That’s the simplest way to delegate events, reducing unnecessary memory overhead by adding event handlers to ancestor elements. But some people will say, your DOM hierarchy is too simple, if MY DOM hierarchy is nested more deep. For example, the li tag contains a div tag, which in turn contains a span tag. What to do in this case? Take it easy. In this situation, we need to get to the heart of the matter. If you think about it, you essentially determine whether the element that triggers the event is bound to the event element itself or its children. How’s that? This analysis, the problem is not readily solved. Node.contains() is used to determine the inclusion relationship between elements. The specific implementation is as follows:

<ul id="list">
    <li class="item" data-id="0">
        <div>
            <span>List item 1</span>
        </div>
    </li><! -... --><li class="item" data-id="99">
        <div>
            <span>List item 100</span>
        </div>
    </li>
</ul>

/ / get the dom
const ul = document.querySelector('#list');
// Add the event handler to the parent element
ul.addEventListener('click'.function (e) {
    if (e.target === this) return;
    let target = e.target;
    // Recursively find the list item
    while (true) {
        if (target.parentNode === this) break;
        target = target.parentNode;
    }
    // Determine the inclusion relation
    if (target.contains(e.target)) {
        console.log(target.dataset.id); }});Copy the code

I want to pass in multiple selectors like jQuery’s on method. How can I do that? It’s not hard to do, just use element.matches () to determine whether the Element is selected by the specified selector string.

implementation

On paper come zhongjue shallow, must know this to practice.

Without further ado, go straight to the code.

<ul id="list">
    <li class="item">
        <div>
            <span>White in Quebec</span>
            <button class="follow" type="button">Focus on</button>
        </div>
        <p>Just write something...</p>
    </li><! -... --><li class="item">
        <div>
            <span>RanBing</span>
            <button class="follow" type="button">Focus on</button>
        </div>
        <p>Just write something...</p>
    </li>
</ul>

/ / get the dom
const ul = document.querySelector('#list');
// Add the event handler to the parent element
ul.addEventListener('click', delegate('li.item,button.follow'.function (e) {
    if (e.triggerTarget.className === 'follow') {
        // Click the Follow button
        // Block the bubble and do not execute the parent element event
        e.stopPropagation();
    } else if (e.triggerTarget.className === 'item') {
        // Click the list item
    }
    console.log(e.triggerTarget);
}));

/** * Event delegate *@param selector string
 * @param func function
 * @returns* /
function delegate(selector, func) {
    return function (event) {
        if (event.target === this) return;
        let target = event.target;
        const selectorList = selector.split(', ');
        // Recursively find the list item
        while (true) {
            for (let i = 0; i < selectorList.length; i++) {
                // Determines whether the element is selected by the specified selector string
                if (target.matches(selectorList[i])) {
                    // Add a triggerTarget attribute to the event object to indicate the element that triggers the event
                    event.triggerTarget = target;
                    // Delete matched selectors to reduce unnecessary loops
                    selectorList.splice(i, 1);
                    // This points to the event delegate element
                    func.call(this, event);
                    break;
                }
            }
            target = target.parentNode;
            // Stop the bubble
            if (this === target || event.cancelBubble) break; }}}Copy the code

A simple Demo renderings. Click the “like”, “comment” and “forward” buttons to execute corresponding events, and click other elements to respond to the list item click events.

Write in the last

Some of the concepts explained in this article are excerpted from advanced Programming in JavaScript (3rd edition). If you want to learn more about this area of knowledge and systematic learning, go to this book.

One last word: The article GIF was made by GifCam. The software is small, only 1.58MB.