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)
- event-name: event name, can be a standard DOM event
- callback: callback function, when the event is triggered, the function will be injected with a parameter of the current event object event
- useCapture: The default is false, which means that the event handler is executed during the bubbling phase
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.
- UI events, triggered when the user interacts with elements on the page, such as: load, scroll
- Focus event, triggered when the element gains or loses focus, such as: blur, focus
- Mouse events, triggered when the user performs an operation on the page through the mouse, such as: dblclick, mouseup
- Wheel event, triggered when a mouse wheel or similar device is used, such as: mousewheel
- Text events, triggered when text is entered in the document, such as: textInput
- Keyboard events, triggered when the user performs an operation on the page through the keyboard, such as: keydown, keypress
- Composition event, triggered when characters are entered for an IME (input method editor), such as: compositionstart
- Change events, triggered when the underlying DOM structure changes, such as: DOMsubtreeModified
- At the same time, DOM3-level events also allow users to customize some 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.
- (1) Capture phase: the phase in which events are propagated from the window object to the target node from top to bottom;
- (2) Target stage: the stage where the real target node is processing the event;
- (3) Bubbling stage: the stage in which events are propagated from the target node to the window object from bottom to top.
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.
- Dynamically bind events
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
- event. preventDefault()
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>
- event.stopPropagation() & event.stopImmediatePropagation()
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.
- event.target & event.currentTarget
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 + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('b').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('c').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
document.getElementById('d').addEventListener('click', function(e) {
console.log(
'target:' + e.target.id + '¤tTarget:' + e.currentTarget.id
)
})
</script>
When we click on the innermost element d, it will output in turn:
target:d¤tTarget:d
target:d¤tTarget:c
target:d¤tTarget:b
target:d¤tTarget: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 .