认识
一、认识
浏览器事件机制/浏览器事件模型 描述了浏览器如何生成事件、传播事件、处理事件、卸载事件的全过程。其中, 浏览器的事件注册和触发机制确实是发布订阅模式的一个经典实现。过程如下:
一、事件产生: 当用户与页面交互(如点击、输入、滚动等)或系统触发特定行为时,浏览器会生成对应的事件 Event
对象, 并将其作为参数传给事件处理函数, event
对象包含许多有用的属性和方法:
-
通用属性:
-
type
: 事件类型(如click
、keydown
等) -
target
: 事件最初触发的元素 -
currentTarget
: 当前处理事件的元素 -
timeStamp
: 事件创建时的时间戳
-
-
控制事件流的方法:
-
preventDefault()
: 阻止默认行为, 或者return false
也可以 -
stopPropagation()
: 阻止事件继续在捕获或冒泡阶段传播 -
stopImmediatePropagation()
: 阻止事件继续传播,并阻止当前元素上的其他相同事件处理程序执行
-
二、事件注册:
-
DOM Level 0
事件处理程序:-
HTML
属性方式<button onclick="handleClick()">点击</button>
-
DOM
属性方式element.onclick = function(event) {
// 处理事件
};
-
-
DOM Level2
事件处理程序: 使用addEventListener
方法可以在指定的元素上注册事件监听器。该方法允许你指定是否在捕获阶段监听(第三个参数传true
)或在冒泡阶段监听(默认或传false
)。element.addEventListener('click', function(event) {
// 处理事件
}, useCapture);
三、事件流/事件传播: 浏览器在处理事件时,会依次经过以下三个阶段:
-
捕获阶段(
Capture Phase
), 事件从根节点window
对象开始,事件沿着DOM
树向下传递,直到到达目标元素。在这个阶段,如果注册了捕获型事件监听器(通过addEventListener
的第三个参数设置为true
),这些监听器就会被触发。 -
目标阶段(
Target Phase
), 事件到达触发事件的那个元素(目标元素)时,会在该元素上触发事件。此时,无论监听器是捕获型还是冒泡型,都可能被执行。 -
冒泡阶段(
Bubbling Phase
), 事件在目标元素上触发后,会开始沿着DOM
树从目标元素向上传递到window
对象。在这个阶段,注册了冒泡型事件监听器(默认或第三个参数为false
)的元素会被依次触发。注意: 并非所有事件都支持冒泡,例如focus
和blur
事件就不支持冒泡。
四、事件处理与控制:
-
阻止默认行为, 通过调用 e
vent.preventDefault()
可以阻止事件的默认操作(例如点击链接后的跳转)。 -
停止事件传播, 使用
event.stopPropagation()
可以阻止事件在DOM
树中进一步传播,无论是捕获还是冒泡阶段。 -
阻止相同事件传播,
stopImmediatePropagation()
: 阻止事件继续传播,并阻止当前元素上的其他相同事件处理程序执行
五、事件委托, 事件委托利用冒泡机制,将事件监听器绑定到父元素上,而不需要在每个子元素上都注册事件监听器。这样不仅减少了内存消耗,而且在动态添加或删除子元素时更为高效。
document.getElementById('parent').addEventListener('click', function(event) {
if (event.target.className === 'child') {
// 处理子元素点击事件
}
});
六、自定义事件, 开发者可以创建和分发自定义事件。因此, 我们可以基于某个 DOM
元素来实现发布订阅进行通信。另外, 自定义事件和内置事件在事件流上没有本质区别,因此在用 document.addEventListener
监听自定义事件时,同样可以使用第三个参数, 既可以是布尔值(useCapture
)也可以是一个包含更多选项的对象(options
)。
// 创建事件
const myEvent = new CustomEvent('myEvent', {
detail: { message: '自定义事件数据' },// 附加的数据,会存储在事件对象的 detail 属性中。通过传递对象、数组或其他数据,可以在事件监听器中获取额外信息。
bubbles: true, // 指定事件是否在 DOM 树中冒泡。如果设置为 true,事件会从触发的目标元素向上层逐级传递;如果为 false,事件只在目标元素上触发。
cancelable: true // 表示该事件是否可以被取消。设置为 true 时,调用 event.preventDefault() 可以阻止事件的默认行为。
});
// 分发事件
element.dispatchEvent(myEvent);
// 监听事件
element.addEventListener('myEvent', function(event) {
console.log(event.detail.message);
});
二、问题
2.1 认识浏览器事件模型?
2.2 认识浏览器事件机制?
2.3 点击 Button 之后, 发生了什么?
浏览器事件机制/浏览器事件模型 的全过程
2.4 e.target 和 e.currentTarget 区别?
e.target
触发事件的元素(指向事件发生的最具体的元素(事件的真正来源))。e.target
可以用来实现事件委托,该原理是通过事件冒泡(或者事件捕获)给父元素添加事件监听
e.currentTarget
当前处理事件的元素(指向绑定事件处理函数的元素)。
2.5 addEventListener 的参数有深入了解吗?
addEventListener(type, listener);
addEventListener(type, listener, options || useCapture);
-
type
: 监听事件的类型,如:'click'/'scroll'/'focus'
-
listener
: 必须是一个实现了 EventListener 接口的对象,或者是一个函数。当监听的事件类型被触发时,会执行 -
options
: 指定 listener 有关的可选参数对象-
capture
: 布尔值,表示listener
是否在事件捕获阶段传播到EventTarget
时触发 -
once
: 布尔值, 表示listener
添加之后最多调用一次, 为true
则listener
在执行一次后会移除 -
passive
: 布尔值, 表示listener
永远不会调用preventDefault()
-
signal
: 可选,AbortSignal
, 当它的abort()
方法被调用时,监听器会被移除。用来中断(移除)事件监听器的。// 创建 AbortController 实例
const controller = new AbortController();
const signal = controller.signal;
// 使用 signal 添加事件监听器
element.addEventListener('click', handleClick, { signal });
// 稍后,当需要移除事件监听器时
controller.abort(); // 所有使用此 signal 的事件监听器都会被移除
-
-
useCapture
: 布尔值,默认为false
,listener
在事件冒泡阶段结束时执行,true
则表示在捕获阶段开始时执行。作用就是更改事件作用的时机,方便拦截或者不被拦截。