preface

This article mainly introduces:

  1. DOM Event Level
  2. The DOM event flow
  3. DOM event Model
  4. The event agent
  5. Common methods and properties of the Event object

1. DOM event level

We handle DOM events differently for different levels of DOM.

The DOM level can be divided into four levels: DOM0 level “usually called DOM0 level before the formation of DOM1 specification”, DOM1 level, DOM2 level and DOM3 level, and DOM events are divided into three levels: DOM0 event handling, DOM2 event handling and DOM3 event handling. As shown in the figure below:

1.DOM 0-level events

Before we learn about DOM0 events, it is necessary to understand the HTML event handler, which is the earliest type of event handler. The code is as follows:

<button type="button" onclick="fn" id="btn">Let me try</button>

<script>
    function fn(a) {
        alert('Hello World');
    }
</script>
Copy the code

So the question is, do YOU want to put parentheses on FN?

In the HTML onclick property, use parentheses. In the JS onclick property, assign the click event without parentheses. Why is that? We speak through facts:

// fn without parentheses<button type="button" onclick="fn" id="btn">Let me try</button>

<script>
    function fn() {
        alert('Hello World');
    }
    console.log(document.getElementById('btn').onclick);
    // Print the result as follows: this function contains fn, and clicking on it does not pop up 1
    Logonclick (event) {fn} */
</script>// fn = fn; // fn = fn<button type="button" onclick="fn()"  id="btn">Let me try</button>
<script>
// Print the result as follows
Logonclick (event) {fn()} */
</script>
Copy the code

In the above code, we trigger the FN method by defining an onclick property directly in the HTML code. The biggest drawback of this type of event handler is that the HTML and JS are strongly coupled. When we need to change the function name, we have to change two things. The advantage, of course, is that you don’t need to manipulate the DOM to bind events.

DOM0 event binding, which binds an element’s event behavior to methods that are executed during the bubble (or target) phase of the current element’s event behavior.

So how do we achieve low coupling between HTML and JS? In this way, there is a DOM0-level processing event that appears to solve this problem. Dom0-level events assign a function to an event-handling property, such as:

<button id="btn" type="button"></button>

<script>
    var btn = document.getElementById('btn');
    
    btn.onclick = function() {
        alert('Hello World');
    }
    
    // btn.onclick = null; Unbundling event
</script>
Copy the code

In this code, we define an ID for a button, and then use JS to get the id of the button, and assign a function to an event handling property onclick. This method is a representation of DOM0 level handling events. We can unbind an event by assigning null to the event handling property. DOM level 0 event handling steps: first find the DOM node, and then assign the handler to the event properties of that node object.

The downside of dom0-level event handlers is that one handler “event” cannot be bound to multiple handlers at the same time. For example, I want to add another function to the button click event.

var btn = document.getElementById('btn');
    
btn.onclick = function() {
    alert('Hello World');
}
btn.onclick = function() {
    alert('No, I did it. Ha, ha, ha.');
}
Copy the code

2. Level DOM2 events

Dom2-level events, based on dom0-level events, compensate for the inability of one handler to bind multiple handlers at the same time, allowing multiple handlers to be added to one handler. That is, you can add as many handlers as you like with DOM2 events, and removeEventListener is used to remove DOM2 events. The code is as follows:

<button type="button" id="btn">Let me try</button>

<script>
    var btn = document.getElementById('btn');

    function fn() {
        alert('Hello World');
    }
    btn.addEventListener('click', fn, false);
    // Unbind the event
    // btn.removeEventListener('click', fn, false);  
</script>
Copy the code

Dom2-level events define two methods, addEventListener and removeEventListener, which are used to bind and unbind events, respectively

target.addEventListener(type, listener[, useCapture]);
target.removeEventListener(type, listener[, useCapture]);
/* The method takes three arguments: the name of the bound event handling property (excluding on), the event handler, and whether to execute the event handler on capture (more about event bubbling and event trapping below) */
Copy the code

Note:

IE8 and later versions do not support addEventListener and removeEventListener. You need to use attachEvent and detachEvent to implement:

// The version below IE8 only supports bubbling events, and does not support event capture, so there is no third parameter
// The method contains two parameters: the name of the bound event handler property (excluding on) and the event handler function
btn.attachEvent('onclick', fn); // Bind the event
btn.detachEvent('onclick', fn); // Unbind the event
Copy the code

3. The DOM3 event

DOM3 events add more event types than DOM2 events. All types are as follows:

  1. UI events, such as Load and Scroll, are triggered when the user interacts with the elements on the page
  2. Focus events, which are triggered when elements gain or lose focus, e.g., blur, focus
  3. Mouse events, such as dbclick and mouseup, are triggered when the user performs an action on the page with the mouse
  4. Scroll wheel event, triggered when a mouse scroll wheel or similar device, such as mousewheel, is used
  5. Text event, triggered when text is entered in a document, such as: textInput
  6. Keyboard events, such as keyDown and keypress, are triggered when a user uses the keyboard to perform operations on the page
  7. A composite event that is fired when a character is entered for the IME (input method editor), such as compositionStart
  8. Change event, triggered when the underlying DOM structure changes, such as DOMsubtreeModified

DOM3 events also allow users to customize some events.

The evolution of the DOM event level has made event handling more complete and rich, and the next problem is the DOM event model mentioned earlier. “Event Bubble and Event Capture”

2. DOM event flow

Why is there a stream of events?

If a click event is registered on a button and a click event is registered on another parent element div, then when we click on a button, the event on the parent element or the event on the button is triggered first. This requires a convention to regulate the execution order of events, which is the process of event execution.

Over the course of the browser’s development, two different specifications emerged

  • Internet Explorer below IE9 uses event bubbles that start with specific receiving elements and then gradually propagate upward to non-specific elements.
  • Netscapte uses event capture, where events are received first by non-specific elements and then by the most specific nodes.
  • In the Web standard formulated by W3C, both schemes are adopted, both event capture and event bubbling can be used.

3. DOM event model

DOM event models are divided into capture and bubble. To each child element and its parent element are propagation. There are three stages of transmission.

(1) Capture phase: the phase in which events propagate from window object to target node from top to bottom;

(2) Target stage: the stage where the real target node is processing the event;

(3) Bubble stage: the stage in which events propagate from the target node to the Window object from bottom to top.

The third argument to addEventListener is to specify whether the event is executed in the capture or bubble phase. True means that the event is executed in the capture phase, and false means that the event is executed in the bubble phase. So what is event bubbling and event capture? This can be explained by the following picture:

1. Event capture

The capture is top-down, with events coming from the Window object, then the document (object), then the HTML tag (from document.documentElement), then the body tag (from document.body), It then follows the normal HTML structure layer by layer, and finally reaches the target element. We only need to change the third argument of addEventListener to true to implement event capture. The code is as follows:

<! -- CSS code -->
<style>
    body{margin: 0; }div{border: 1px solid # 000; }#grandfather1{width: 200px;height: 200px; }#parent1{width: 100px;height: 100px;margin: 0auto; }#child1{width: 50px;height: 50px;margin: 0auto; }</style>

<! -- HTML code -->
<div id="grandfather1">grandpa<div id="parent1">father<div id="child1">son</div>
    </div>
</div>

<!-- JS 代码 -->
<script>
    var grandfather1 = document.getElementById('grandfather1'),
        parent1 = document.getElementById('parent1'),
        child1 = document.getElementById('child1');
    
    grandfather1.addEventListener('click'.function fn1(){
        console.log('grandfather');
    },true)
    parent1.addEventListener('click'.function fn1(){
        console.log('daddy');
    },true)
    child1.addEventListener('click'.function fn1(){
        console.log('son');
    },true)

    /* When I click on son, do I click on father and grandpa? When I click on son, do the three functions call */
    Fn1 fn2 fn3
    // fn1 fn2 fn3 or fn3 fn2 fn1  
</script>
Copy the code

Let’s start with the results:

When we click on the div tag with ID child1, we print grandpa => Dad => son, which is the exact opposite of event bubble.

2. Event bubble

An event bubble is a bubble of events rising from where they started. We simply need to change the third argument of addEventListener to false to implement event bubbling. The code is as follows:

// The HTML and CSS code is the same as above, and the JS code is only modified
var grandfather1 = document.getElementById('grandfather1'),
    parent1 = document.getElementById('parent1'),
    child1 = document.getElementById('child1');

grandfather1.addEventListener('click'.function fn1(){
    console.log('grandfather');
},false)
parent1.addEventListener('click'.function fn1(){
    console.log('daddy');
},false)
child1.addEventListener('click'.function fn1(){
    console.log('son');
},false)

/* When I click on son, do I click on father and grandpa? When I click on son, do the three functions call */
Fn1 fn2 fn3
// fn1 fn2 fn3 or fn3 fn2 fn1  
Copy the code

Let’s start with the results:

For example, in the image above, the div tag with ID child1 is the event target. Clicking on the div tag will also trigger the click event on the parent level, one layer at a time up to the outermost HTML or Document.

Note: When the third argument is false or null, it indicates binding during the bubble phase.

Iv. Event Agent (Event commission)

1. What does the event agent mean and why should it be optimized?

Since events will propagate upward to the parent node in the bubble phase, the listening function of the child node can be defined on the parent node, and the listening function of the parent node can handle the events of multiple child elements in a unified manner. This method is called delegating events.

For example, one way is for students in a dormitory to receive the express at the same time. Another way is to entrust the matter to the dormitory leader, and ask one person to go out and pick up all the express, and then distribute it to each student according to the recipient.

Here, picking up the express is an event, each student refers to the DOM element that needs to respond to the event, and the dormitory leader who goes out to receive the express is the agent element, so the real binding event is this element, and the process of sending the express according to the recipient is in the event execution. You need to determine which or more of the propped elements the events of the current response should match.

There are some optimizations we can make to the event binding using the mechanism of event bubbling or capture. In JS, if we register more and more events, the performance of the page will get worse and worse because:

  • Functions are objects and consume memory, and the more objects in memory, the worse the browser performance
  • The registered events typically specify DOM elements, and more events result in more DOM elements being accessed, delaying the page interaction ready time.
  • The deletion binding event is not considered when deleting a child element

2. The advantages

  • Reduce memory consumption and improve performance

Suppose we have a list with a large number of list items, and we need to respond to an event when each list item is clicked

// 例4
<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>.<li>item n</li>
</ul>
Copy the code

If you bind a function to each of the list items one by one, the memory consumption is very high and the efficiency is very high. Using event agent, we only need to give the parent container ul binding approach, so no matter which a descendant elements, click will be according to the transmission mechanism of transmission of bubbling, click behavior triggers the container, and then the corresponding method to carry out, according to the event source, we can know is who, click to do different things.

  • Dynamic binding event

By user operation in many cases, we need to dynamically add or delete a list item element, if give each child element binding events at the beginning, then in the list of changes, just need to give new elements of the binding event, to tie events is about to delete the elements of the solution, if use event agency will save a lot of trouble.

2. How

Let’s delegate the event of the li element to its parent element:


<ul id="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

<script>
// Bind the event to the parent element
document.getElementById('list').addEventListener('click'.function (e) {
    // Compatibility processing
    var event = e || window.event;
    var target = event.target || event.srcElement;
    // Determine whether the target element is matched
    if (target.nodeName.toLocaleLowerCase() === 'li') {
        console.log('the content is: ', target.innerHTML); }});</script>
Copy the code

This is a common way to implement event delegates, but this method has a BUG. If the listener has a child element, the event will be invalid if we click on the child element, so we can use the bubbling event propagation mechanism mentioned in the previous section of this article to solve this BUG. Improved event delegate code:

<ul id="list">
    <li>1 <span>aaaaa</span></li>
    <li>2 <span>aaaaa</span></li>
    <li>3 <span>aaaaa</span></li>
    <li>4</li>
</ul>

<script>


// Bind the event to the parent element
document.getElementById('list').addEventListener('click'.function (e) {
    // Compatibility processing
    var event = e || window.event;
    var target = event.target || event.srcElement;
    // Determine whether the target element is matched
    /* From the target (click) element up to the currentTarget (listen) element. If an element is found, the event is raised. If not, null */ is returned
    while(target.tagName ! = ='LI') {if(target.tagName === 'UL'){
            target = null
            break;
        }
        target = target.parentNode
    }
    if (target) {
    console.log('You clicked on li in ul')}});Copy the code

Common methods and properties of the Event object

1.event. preventDefault()

If this method is called, the default event behavior is no longer triggered. What is the default event? For example, click the submit button on the form to refresh the page, a TAB default page jump, or anchor location, etc.

Usage Scenario 1: Using the A TAB is simply intended to be a normal button that you click to implement a function. You do not want to jump to the page, nor do you want to locate the anchor point.

Methods a

<a href="javascript:;">link</a>
Copy the code

Method 2

When we click on the A tag, it triggers the click event first, and then it performs its default behavior

<a id="test" href="http://www.google.com">link</a>
<script>
    test.onclick = function(e){
        e = e || window.event;
        return false;
    }
</script>
Copy the code

Methods three

<a id="test" href="http://www.google.com">link</a>
<script>
    test.onclick = function(e){
        e = e || window.event;
        e.preventDefault();
    }
</script>
Copy the code

Usage Scenario 2: A maximum of six characters can be entered in the input box.

The implementation code is as follows:

<input type="text" id='tempInp'>
<script>
    tempInp.onkeydown = function(ev) {
        ev = ev || window.event;
        let val = this.value.trim() //trim removes whitespace from first string (incompatible)
        . / / this value = this. Value. The replace (/ ^ + | + $/ g, ' ') compatible with writing
        let len = val.length
        if (len >= 6) {
            this.value = val.substr(0.6);
            // Prevent default behavior to remove special keys (DELETE\ back-space \ arrow keys...)
            let code = ev.which || ev.keyCode;
            if (!/ ^ (46 8 37 | | | | | 39 38, 40) $/.test(code)) {
                ev.preventDefault()
            }
        }
    }
</script>
Copy the code

2.event.stopPropagation() & event.stopImmediatePropagation()

The event.stopPropagation() method prevents events from bubbling to the parent element, preventing any parent event handlers from being executed. The demo code is as follows:

// Modify the event Bubble demo code
child1.addEventListener('click'.function fn1(e){
    console.log('son');
    e.stopPropagation()
},false)
Copy the code

StopImmediatePropagation can not only prevent an event from bubbling to its parent element, but also prevent other listeners of the same event type from being triggered. While stopPropagation can only achieve the former effect. Let’s look at an example:

<button id="btn">Let me try</button>
<script>
const btn = document.querySelector('#btn');
btn.addEventListener('click', event => {
  console.log('btn click 1');
  event.stopImmediatePropagation();
});
btn.addEventListener('click', event => {
  console.log('btn click 2');
});
document.body.addEventListener('click', () = > {console.log('body click');
});
</script>
Copy the code

According to the printed results, we found that after using stopImmediatePropagation, when clicking the button, not only would the body binding event not be triggered, but also the other click event of the button would not be triggered.

3.event.target & event.currentTarget

As you can see from the above image, event.target refers to the element that triggered the event, and event.currentTarget is the element that binds the event.

conclusion

Therefore, it is not necessary to remember when E. cap and E. cap are equal and when they are not, but to understand who they are referring to.

  • e.targetPoints to the object that triggers the event listener “the true sender of the event.”
  • e.currentTargetPoints to the object “Listener” that added the listener event.

6. Reference articles

  • DOM Events

  • DOM event mechanism

  • The event model

  • JavaScript event delegate detail

  • The difference between event.target and event.currentTarget

  • DOM event mechanism