JavaScript节点事件

本文最后更新于 2025年9月9日 下午

参考:
https://juejin.cn/post/6844903445418213383
https://juejin.cn/post/6844904097372438542

事件绑定

要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数。事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称。有三种绑定事件的方法:

  • 在 DOM 元素中直接绑定事件
    在 DOM 元素上绑定 onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup 等事件。

  • 在 JavaScript 代码设置事件处理程序
    在 JavaScript 代码中(即 script 标签内)绑定事件可以使 JavaScript 代码与 HTML 标签分离,文档结构清晰,便于管理和开发。

  • 使用事件监听绑定事件
    用 addEventListener 和 removeEventListener 来绑定和移除事件监听函数。

事件监听

对于事件传播,W3C 规范中定义了 3 个事件阶段,先后顺序是捕获阶段、目标阶段、冒泡阶段:

  • 事件捕获:事件由文档根节点接收,然后逐级向下传播到被操作的具体节点
  • 事件冒泡:事件由被操作的具体节点接收,然后逐级向上传播到文档根节点。与事件捕获相反

W3C 规范

1
2
element.addEventListener(event, function, useCapture); // 绑定
element.removeEventListener(event, function, useCapture); // 移除

两个函数的参数要求都是一样的:

  • event(必需):事件名,支持所有 DOM 事件
  • function(必需):指定事件触发时要执行的函数
  • useCapture(可选):指定事件是在捕获还是冒泡阶段执行。false 冒泡阶段(默认),true 捕获阶段。也可以传入对象:如果这个对象的 capture 属性为 true,那么函数就会被注册为捕获处理程序;如果这个属性为 false 或省略该属性,那么处理程序就不会注册到捕获阶段。如果对象有 once 属性且值为 true,那么事件监听器会在被触发一次后会自动移除;如果值为 false 或省略该属性,那么处理程序永远不会被自动移除

事件监听优点

  • 可以解除相应的绑定
  • 可以绑定多个不同的事件(按照绑定顺序依次调用)
  • 事件监听不影响在 JavaScript 代码设置事件处理程序

封装事件监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 绑定监听事件
function addEventHandler(target, type, fn) {
if (target.addEventListener) {
target.addEventListener(type, fn);
} else {
target.attachEvent("on" + type, fn);
}
}
// 移除监听事件
function removeEventHandler(target, type, fn) {
if (target.removeEventListener) {
target.removeEventListener(type, fn);
} else {
target.detachEvent("on" + type, fn);
}
}
//测试
const btn5 = document.getElementById("btn5");
addEventHandler(btn5, "click", hello1); // 添加事件hello1
addEventHandler(btn5, "click", hello2); // 添加事件hello2
removeEventHandler(btn5, "click", hello1); // 移除事件hello1

事件委托

事件委托利用冒泡(也可用捕获),将元素的事件委托给它的父元素或祖先元素上,触发执行效果。

1
2
3
4
5
<ul id="myLink">
<li id="1">aaa</li>
<li id="2">bbb</li>
<li id="3">ccc</li>
</ul>

不使用事件委托

1
2
3
4
5
6
7
8
9
10
const myLink = document.getElementById("myLink");
const li = myLink.getElementsByTagName("li");

for (let i = 0; i < li.length; i++) {
li[i].onclick = function (e) {
let e = event || window.event;
let target = e.target || e.srcElement;
alert(e.target.id + ":" + e.target.innerText);
};
}

缺点:

  • 给每一个列表都绑定事件,消耗内存
  • 当有动态添加的元素时,需要重新给元素绑定事件

使用事件委托

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function delegate(element, eventType, selector, fn) {
element.addEventListener(eventType, (e) => {
let el = e.target;
while (!el.matches(selector)) {
if (element === el) {
el = null;
break;
}
el = el.parentNode;
}
el && fn.call(el, e, el);
});
return element;
}

优点:

  • 提高性能,减少内存占用
  • 动态添加 DOM 元素时,不需要因元素的改动而修改事件绑定

注意:

  • 事件委托的实现依靠的冒泡,因此不支持事件冒泡的事件不适合使用事件委托
  • 不是所有事件绑定都适合事件委托,不当使用反而可能导致不需要绑定事件的元素也被绑定事件

JavaScript节点事件
https://xuekeven.github.io/2021/09/13/JavaScript节点事件/
作者
Keven
发布于
2021年9月13日
更新于
2025年9月9日
许可协议