MEMEPh. ideas that are worth sharing...

DOM Event Mechanism

Foreword


This article mainly introduces the common applications of DOM event level, DOM event model, event flow, event proxy and Event object. I hope it will help and inspire you!

 

1. DOM event level


The DOM level can be divided into four levels: DOM0 level, DOM1 level, DOM2 level and DOM3 level. The DOM events are divided into three levels: DOM level 0 event processing, DOM level 2 event processing and DOM level 3 event processing . There are no DOM level 1 events since there is no event-related content in DOM level 1.

 

1. DOM level 0 events

el.onclick=function(){}

var btn = document.getElementById('btn');
 btn.onclick = function(){
     alert(this.innerHTML);
 }

When you want to bind multiple events of the same type to the same element/label (such as binding 3 click events to the btn element above), it is not allowed . DOM0 event binding, binding methods to the element's event behavior, these methods are executed in the bubbling phase (or target phase) of the current element's event behavior .

 

2. DOM Level 2 Events

el.addEventListener(event-name, callback, useCapture)
var btn = document.getElementById('btn');
btn.addEventListener("click", test, false);

function test(e){
    e = e || window.event;
    alert((e.target || e.srcElement).innerHTML);
    btn.removeEventListener("click", test)
}
//IE9-:attachEvent()and detachEvent()。
//IE9+/chrom/FF:addEventListener and removeEventListener()

IE browsers below IE9 do not support addEventListener() and removeEventListener(), use attachEvent() and detachEvent() instead, because IE9 and below do not support event capture, so there is no third parameter, before the first event name To add on.

 

3. DOM Level 3 Events

Added more event types on top of DOM level 2 events.

2. DOM event model and event flow

The DOM event model is divided into capturing and bubbling . When an event occurs, it is propagated between child and parent elements. This propagation is divided into three stages.

 

The specific process of DOM event capture

The capture is from top to bottom, events first go from the window object, then to the document (object), then to the html tag (getting the html tag via document.documentElement), then the body tag (getting the body tag via document.body), then It is passed down layer by layer according to the ordinary html structure, and finally reaches the target element.

The process of event bubbling is just the reverse process of event capture.

Next, let's look at an example of event bubbling:

 

<div id="outer">
    <div id="inner"></div>
</div>

......

window.onclick = function() {
    console.log('window');
};

document.onclick = function() {
    console.log('document');
};

document.documentElement.onclick = function() {
    console.log('html');
};

document.body.onclick = function() {
    console.log('body');
}

outer.onclick = function(ev) {
    console.log('outer');
};

inner.onclick = function(ev) {
    console.log('inner');
};

As we mentioned above, the onclick binding method to the element's event behavior is executed in the bubbling phase (or target phase) of the current element's event behavior.

 

3. Event proxy (event delegation)


Since the event will be propagated up to the parent node in the bubbling stage, the listener function of the child node can be defined on the parent node, and the listener function of the parent node can uniformly handle the events of multiple child elements. This approach is called event delegation.

 

1. Advantages

Reduce memory consumption and improve performance

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

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

If you bind a function to each list item, it will consume a lot of memory and consume a lot of performance in terms of efficiency. With the help of event proxy, we only need to bind methods to the parent container ul, so that no matter which descendant element is clicked, the click behavior of the container will be triggered according to the transmission mechanism of bubbling propagation, and then the corresponding method will be executed. Depending on the event source, we can know who clicked and do different things.

In many cases, we need to dynamically add or delete list item elements through user operations. If we bind events to each child element at the beginning, then when the list changes, we need to re-bind events to the newly added elements, so that the elements that are about to be deleted will be bound with events. The element unbinds the event, if you use the event proxy, you will save a lot of such trouble.

 

2. How to implement

Next, let's implement the event delegation of the li element under the parent element #list in the above example to its parent element:

 

document.getElementById('list').addEventListener('click', function (e) {
  var event = e || window.event;
  var target = event.target || event.srcElement;
  if (target.nodeName.toLocaleLowerCase === 'li') {
    console.log('the content is: ', target.innerHTML);
  }
});

 

Fourth, the common application of Event objects


If this method is called, the default event behavior will no longer fire . What is the default event? For example, when the form clicks the submit button (submit) to jump to the page, the default page of the a tag jumps or the anchor point is positioned.

Many times we use the a tag just to act as an ordinary button, click to achieve a function, do not want to jump the page, and do not want anchor positioning.

//method one:
<a href="javascript:;">Link</a>

It can also be blocked by JS method, and bind the method to its click event. When we click on the A tag, the click event will be triggered first, and then the default behavior will be executed.

//Method Two:
<a id="test" href="http://www.cnblogs.com">Link</a>
<script>
test.onclick = function(e){
e = e || window.event;
return false;
}
</script>

/

//Method three:
<a id="test" href="http://www.rendc.com">Link</a>
<script>
test.onclick = function(e){
e = e || window.event;
e.preventDefault();
}
</script>

Next, let's look at an example: the input box can only enter up to six characters, how to achieve it?

// Example 5

<input type="text" id='tempInp'>
<script>
tempInp.onkeydown = function(ev) {
ev = ev || window.event;
let val = this.value.trim() //trim removes the first space in the string (incompatible)

// this.value=this.value.replace(/^ +| +$/g,'') Compatible notation
let len ​​= val.length
if (len >= 6) {
this.value = val.substr(0, 6);

//Prevent the default behavior to remove special keys (DELETE\BACK-SPACE\Arrow keys...)
let code = ev.which || ev.keyCode;
    if (!/^(46|8|37|38|39|40)$/.test(code)) {
ev.preventDefault()
} 
} 
} 

</script>

The event.stopPropagation() method stops the event from bubbling up to the parent element, preventing any parent event handlers from being executed . The event bubbling phase mentioned above refers to the phase in which events are propagated from the target node to the window object from bottom to top.

On the click event of the inner element in example 3, event.stopPropagation()after adding this sentence, the execution of the parent event is blocked, and finally only 'inner' is printed.

 inner.onclick = function(ev) {
    console.log('inner');
    ev.stopPropagation();
};

stopImmediatePropagation stops the event from bubbling up to the parent element, as well as other listeners of the same event type on the element from being fired. And stopPropagation can only achieve the former effect . Let's look at an example:

<body>
  <button id="btn">click me to stop propagation</button>
</body>
....
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');
});
// btn click 1

As shown above, after using stopImmediatePropagation, when the button is clicked, not only the body binding event will not trigger, but also another click event of the button will not trigger.

To be honest, the difference between the two is not easy to describe in words. Let's look at an example first:

<div id="a">
  <div id="b">
    <div id="c"><div id="d"></div></div>
  </div>
</div>

<script>
  document.getElementById('a').addEventListener('click', function(e) {
    console.log(
      'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id
    )
  })

  document.getElementById('b').addEventListener('click', function(e) {
    console.log(
      'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id
    )
  })

  document.getElementById('c').addEventListener('click', function(e) {
    console.log(
      'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id
    )
  })

  document.getElementById('d').addEventListener('click', function(e) {
    console.log(
      'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id
    )
  })
</script>

 

When we click on the innermost element d, it will output in turn:

target:d&currentTarget:d
target:d&currentTarget:c
target:d&currentTarget:b
target:d&currentTarget:a

From the output, we can see that event.target the element that points to the triggering event is the element event.currentTarget bound by the event, and only the target element that is clicked event.target will be equal event.currentTarget. That is, event.currentTarget it is always the one listening to the event, event.targetbut the real sender of the event .