Skip to main content

事件机制

可以通过 EventCustomEvent 定义事件,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);

关于EventCustomEvent详见 Event-MDNCustomEvent-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)的主要编程方式

事件传播

  1. 捕获阶段:从 window 对象传导到目标节点
  2. 目标阶段:在目标节点上触发事件
  3. 冒泡阶段:从目标节点传导回 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");
});