Web APIs - Day 3
Learn more about event advancement, achieve more interactive web page special effects, and optimize the efficiency of event execution by combining the characteristics of event streams.
- Learn how to stop events from bubbling up
- Understand the implementation principles of event delegation
event stream
Event flow is a description of the event execution process. Understanding the event execution process can help deepen the understanding of events and improve the flexibility of event use in development practice.
As shown in the figure above, any event will always go through two stages when it is triggered: [capture stage] and [bubbling stage].
In short, the capture stage is the transmission process [from father to son], and the bubbling stage is the transmission process [from son to father].
capture and bubble
After understanding what event flow is, let's look at how event flow affects event execution:
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner">
<div class="child"></div>
</div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer');
const inner = document.querySelector('.inner');
const child = document.querySelector('.child');
// html 元素添加事件
document.documentElement.addEventListener('click', function () {
console.log('html...')
})
// body 元素添加事件
document.body.addEventListener('click', function () {
console.log('body...')
})
// 外层的盒子添加事件
outer.addEventListener('click', function () {
console.log('outer...')
})
// 中间的盒子添加事件
outer.addEventListener('click', function () {
console.log('inner...')
})
// 内层的盒子添加事件
outer.addEventListener('click', function () {
console.log('child...')
})
</script>
</body>
After executing the above code, we found that when the click event is triggered, the click events of its ancestor elements are also triggered one after another. Why is this?
Combined with the characteristics of event flow, we know that when an event of an element is triggered, the event will always pass through its ancestors before reaching the current element, and then passed from the current element to the ancestors. The event encounters the same event during the flow process. The event will be triggered.
Another detail that we should pay attention to is the [execution order] in which events are triggered one after another. The execution order of events is controllable, that is, it can be executed in the capture phase or in the bubbling phase.
If the event is executed in the bubbling stage, we call it bubbling mode. It will execute the child box event first and then the parent box event. The default is bubbling mode.
If the event is executed in the capture phase, we call it capture mode. It will execute the parent box event first and then execute the child box event.
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner"></div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer')
const inner = document.querySelector('.inner')
// 外层的盒子
outer.addEventListener('click', function () {
console.log('outer...')
}, true) // true 表示在捕获阶段执行事件
// 中间的盒子
outer.addEventListener('click', function () {
console.log('inner...')
}, true)
</script>
</body>
in conclusion:
addEventListener
The third parameter determines whether the event is triggered in the capture phase or the bubbling phase.addEventListener
The third parameter is,true
which indicates the capture phase trigger,false
which indicates the bubbling phase trigger. The default value isfalse
- Event flow will only have an effect if the parent and child elements have the same event type
- Most scenes use the default bubbling mode (one of the reasons is that early IE did not support capture)
Stop bubbling
Preventing bubbling means blocking the flow of events to ensure that events are only executed on the current element and no longer affect its corresponding ancestor elements.
<body>
<h3>阻止冒泡</h3>
<p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。</p>
<div class="outer">
<div class="inner">
<div class="child"></div>
</div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer')
const inner = document.querySelector('.inner')
const child = document.querySelector('.child')
// 外层的盒子
outer.addEventListener('click', function () {
console.log('outer...')
})
// 中间的盒子
inner.addEventListener('click', function (ev) {
console.log('inner...')
// 阻止事件冒泡
ev.stopPropagation()
})
// 内层的盒子
child.addEventListener('click', function (ev) {
console.log('child...')
// 借助事件对象,阻止事件向上冒泡
ev.stopPropagation()
})
</script>
</body>
Conclusion: The method in the event object ev.stopPropagation
is specifically used to prevent events from bubbling.
Mouse over event:
mouseover and mouseout will have a bubbling effect
mouseenter and mouseleave have no bubbling effect (recommended)
event delegation
Event delegation is a knowledge and skill that uses the characteristics of event streams to solve some practical development needs. Its main function is to improve program efficiency.
A large number of event monitoring is relatively performance-intensive, as shown in the following code
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) {
// 为 10000 个 button 元素添加了事件
buttons.addEventListener('click', function () {
// 省略具体执行逻辑...
})
}
</script>
Using the characteristics of the event stream, the above code can be optimized. The bubbling mode of the event will always flow the event to its parent element. If the parent element listens to the same event type, then the event of the parent element will be triggered. And execute, it is this feature that is used to optimize the above code, as shown in the following code:
<script>
// 假设页面中有 10000 个 button 元素
let buttons = document.querySelectorAll('table button');
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
let parents = document.querySelector('table');
parents.addEventListener('click', function () {
console.log('点击任意子元素都会触发事件...');
})
</script>
Our ultimate goal is to ensure that the callback function of the event is executed only when the button sub-element is clicked. How to determine which sub-element the user clicks?
target
The attribute or attribute in the event object srcElement
represents the element that actually triggered the event, which is a node of element type.
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button')
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
const parents = document.querySelector('table')
parents.addEventListener('click', function (ev) {
// console.log(ev.target);
// 只有 button 元素才会真正去执行逻辑
if(ev.target.tagName === 'BUTTON') {
// 执行的逻辑
}
})
</script>
The optimized code only adds event listeners to ancestor elements, which is much more efficient than adding event listeners to 10,000 elements! ! !
Other events
page load event
Event triggered when external resources (such as images, external CSS and JavaScript, etc.) are loaded.
Sometimes you need to wait for all page resources to be processed to do something
Event name: load
All resources on the monitoring page have been loaded:
window.addEventListener('load', function() {
// xxxxx
})
element scroll event
Events that are continuously triggered when the scroll bar is scrolling
window.addEventListener('scroll', function() {
// xxxxx
})
page size event
Events will be triggered when the window size changes:
window.addEventListener('resize', function() {
// xxxxx
})
Element size and position
Get the element's own width and height, including the width and height, padding, and border set by the element itself.
offsetWidth和offsetHeight
What is obtained is a numerical value, which is convenient for calculation.
Note: What is obtained is the visible width and height. If the box is hidden, the obtained result is 0.
inch incident
Events will be triggered when the window size changes:
window.addEventListener('resize', function() {
// xxxxx
})
Element size and position
Get the element's own width and height, including the width and height, padding, and border set by the element itself.
offsetWidth和offsetHeight
What is obtained is a numerical value, which is convenient for calculation.
Note: What is obtained is the visible width and height. If the box is hidden, the obtained result is 0.