事件机制
可以通过 Event
和 CustomEvent
定义事件,CustomEvent
继承自 Event
对象,并且可以传递额外的数据,而且可以在 Web Worker 中使用
let myEvent = new Event("myEvent", { bubbles: true, cancelable: false });
// 触发事件
document.dispatchEvent(myEvent);
const customEvent = new CustomEvent("myCustomEvent", {
detail: { name: "lucas" },
});
document.addEventListener("myCustomEvent", function (event) {
console.log("Custom Event Triggered:", event.detail.name);
});
document.dispatchEvent(customEvent);
关于
Event
和CustomEvent
详见 Event-MDN 和 CustomEvent-MDN
DOM 节点的事件操作(监听和触发),都定义在 EventTarget
接口上,该接口主要提供三个实例方法:
addEventListener()
:绑定事件的监听函数removeEventListener()
:移除事件的监听函数dispatchEvent()
:触发事件
事件发生以后,会产生一个事件对象,并作为参数传给事件的监听函数。浏览器原生提供一个 Event
对象,所有的事件都是这个对象的实例,事件对象常用的方法有:
Event.preventDefault()
。阻止事件的默认动作Event.stopPropagation()
。阻止事件向上冒泡Event.stopImmediatePropagation()
。不再执行同一事件其他的监听事件Event.composedPath()
。返回一个数组,成员是事件的最底层节点和依次冒泡经过的所有上层节点
监听事件的方法
addEventListener
。可以定义多个相同事件,并指定冒泡或捕获阶段触发节点.on+方法(click)
,比如dom.onclick=()=>{}
- 定义 html 节点事件属性
<div onclick="say()"></div>
以上几种监听事件的回调函数 this 均指向 dom 节点
事件模型
浏览器的事件模型,指的是事先监听某些事件,然后在事件发生后,就会执行对应的监听函数。这是事件驱动编程模式(event-driven)的主要编程方式
事件传播
- 捕获阶段:从
window
对象传导到目标节点 - 目标阶段:在目标节点上触发事件
- 冒泡阶段:从目标节点传导回
window
对象
事件代理
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理
/*
语法:addEventListener(element, type, fn, selector)
说明:如果selector没有,直接给element绑定事件,
如果selector有,将selector对应的多个元素的事件委托绑定给父元素element
*/
export function addEventListener(element, type, fn, selector) {
// 如果没有指定selector, 普通的事件绑定
if (!selector) {
element.addEventListener(type, fn);
} else {
// 否则是代委托的事件绑定
element.addEventListener(type, function (event) {
// 得到真正发生事件的目标元素
const target = event.target;
// 如果与选择器匹配
if (target.matches(selector)) {
// 调用处理事件的回调fn, 并指定this为目标元素, 参数为event
fn.call(target, event);
}
});
}
}
存在局限性:
- focus、blur 这些事件没有事件冒泡机制,所以无法进行委托绑定事件
- mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合于事件委托的
事件类型
- 鼠标事件
- 键盘事件
- 进度事件
- 表单事件
- 触摸事件
- 拖拉事件
- 其他常见事件
- load/DomContentloaded
- error
自定义事件
比如定义 popState 事件:
const _historyWrap = function (type) {
const orig = history[type];
const e = new Event(type);
return function () {
const rv = orig.apply(this, arguments);
e.arguments = arguments;
window.dispatchEvent(e);
return rv;
};
};
history.pushState = _historyWrap("pushState");
history.replaceState = _historyWrap("replaceState");
window.addEventListener("pushState", function (e) {
console.log("change pushState");
});
window.addEventListener("replaceState", function (e) {
console.log("change replaceState");
});