为什么要使用 addEventListener() ?
从之前学习可以知道,我们可以使用事件属性(如 onclick、onmouseover 等)来为某个元素添加一个事件。不过这种方式有一个很大的弊端,那就是:一个事件属性只能添加一个处理程序,后面添加的处理程序会覆盖前面的。先来看一个例子。
示例 1:为同一个事件属性添加多个处理程序
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">按钮</button>
<script>
const oBtn = document.getElementById("btn");
oBtn.onclick = function() {
alert("第 1 次");
};
oBtn.onclick = function() {
alert("第 2 次");
};
oBtn.onclick = function() {
alert("第 3 次");
};
</script>
</body>
</html>运行之后,页面效果如下图 1 所示。当我们点击【按钮】之后,会弹出一个对话框,如下图 2 所示。


分析:
在这个例子中,我们一开始的目的是想给按钮添加 3 次 onclick 事件,但 JavaScript 最终只会执行最后一次 onclick。从上面也可以看出来了,使用事件属性这种方式是没办法为一个元素添加多个相同事件的。
小伙伴们可能会在心里问:“那又如何?我们一般也不会为同一个元素添加多个相同事件啊。” 没错,对于同一个元素来说,确实很少需要添加多个相同事件的。可是,有些情况下确实需要这么做才行。比如在点击【提交表单】按钮时,需要验证由用户输入的全部数据,然后再通过 AJAX 将其提交给服务器。
如果想要为一个元素添加多个相同的事件,该如何实现呢?这就需要用到另外一种添加事件的方式了,那就是使用 addEventListener()。
JavaScript addEventListener() 语法
在 JavaScript 中,addEventListener() 方法解决了上面的问题,它具有以下主要优势:
- 允许为同一个事件附加多个处理程序:我们可以使用 addEventListener() 为一个元素的同一个事件(如点击事件)添加多个不同的处理函数,它们会按照添加的顺序依次执行,而不会存在相互覆盖问题。
- 支持事件冒泡和事件捕获:addEventListener() 提供了第 3 个参数,可以让我们控制事件处理程序是在 “事件冒泡” 阶段触发,还是在 “事件捕获” 阶段触发。
- 可以更方便地移除事件监听器:我们可以通过 removeEventListener() 方法,来很方便地移除之前添加的监听器。
- 更好的代码分离:将 JavaScript 事件处理代码与 HTML 结构完全分离,提高代码的可维护性。
语法:
obj.addEventListener(event, handler, options);说明:
obj 是一个 DOM 对象。addEventListener() 接收以下 3 个参数。
event(必选):是一个字符串,表示要监听的事件类型。比如点击事件是 "click",鼠标移入事件是 "mouseover"。注意,这个事件类型是不需要加上 “on” 前缀的。handler(必选):是一个函数,当指定的事件发生时,该函数会被调用执行。这个函数通常接收一个 event 对象作为参数,包含事件的详细信息。options ( 可选 ):是一个对象,用于配置事件监听器的行为。该对象可以包含以下属性:capture: 该属性是一个布尔值,默认值为 false。如果设置为 true,则表示在 “事件捕获“ 阶段触发处理程序;如果设置为 false 或省略,则表示在 “事件冒泡” 阶段触发处理程序。once: 该属性是一个布尔值。如果设置为 true,则表示事件处理程序在触发一次后会自动移除。passive: 该属性是一个布尔值。如果设置为 true,则表示事件处理程序不会调用 preventDefault()。一般常用于提高滚动性能。
提示: addEventListener() 是一个方法,它是 DOM 对象的一个方法。
JavaScript addEventListener() 示例
接下来,我们通过几个简单的例子来讲解 JavaScript addEventListener() 是如何使用的。
示例 2:addEventListener() 为一个按钮添加点击事件监听器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">欢迎</button>
<script>
const oBtn = document.getElementById("btn");
oBtn.addEventListener("click", function() {
alert("欢迎来到绿叶网!");
});
</script>
</body>
</html>运行之后,页面效果如下图 1 所示。当点击【欢迎】按钮之后,会弹出一个对话框,如下图 2 所示。


分析:
对于 addEventListener() 中的事件处理函数,如果内部逻辑代码比较多时,我们也可以单独拿出来放在别处定义。下面 2 种方式是等价的,小伙伴们可以试一下。
// 方式 1
oBtn.addEventListener("click", function() {
alert("欢迎来到绿叶网!");
});
// 方式 2
function welcome() {
alert("欢迎来到绿叶网!");
}
oBtn.addEventListener("click", welcome);示例 3:addEventListener() 为一个元素添加多个相同事件的监听器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">欢迎</button>
<script>
const oBtn = document.getElementById("btn");
oBtn.addEventListener("click", function() {
alert("第 1 次");
});
oBtn.addEventListener("click", function() {
alert("第 2 次");
});
oBtn.addEventListener("click", function() {
alert("第 3 次");
});
</script>
</body>
</html>运行之后,页面效果如下图所示。当点击【欢迎】按钮之后,会连续弹出 3 次不同的对话框。

分析:
通过使用 addEventListener(),我们可以为同一个按钮的 “click” 事件添加了 3 个独立的事件处理程序,它们都会在点击按钮时依次执行。
示例 4:事件冒泡
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="outer">
外部 div
<div id="inner">
内部 div
</div>
</div>
<script>
const oOuter = document.getElementById("outer");
const oInner = document.getElementById("inner");
// 为外部 div 添加 click 事件
oOuter.addEventListener("click", function() {
alert("外部 div 被点击了!");
});
// 为内部 div 添加 click 事件
oInner.addEventListener("click", function() {
alert("内部 div 被点击了!");
});
</script>
</body>
</html>
页面效果如下图所示。

分析:
当我们点击 “内部 div” 时,会依次弹出两个对话框,如下图 1 和图 2 所示。


首先我们要清楚一件事,外部 div 是包裹着内部 div 的。因此当我们点击 “内部 div” 时,其实也相当于点击了 “外部 div”(因为内部 div 属于外部 div 的一部分)。然后再默认情况下,页面会采用事件冒泡的方式来触发。因此,内部 div 的 click 会被先触发,然后外部 div 的 click 后触发。
如果我们为外部 div 的 addEventListener() 添加 { capture: true } 选项,也就是改为下面代码:
oOuter.addEventListener("click", function() {
alert("外部 div 被点了");
}, { capture: true });然后再次点击 “内部 div”,然后会发现弹出对话框的顺序变了。其中,顺序如下图 1 和图 2 所示。


事件冒泡与事件捕获
事件冒泡 (Bubbling):就像水底的气泡往上冒一样,事件默认是从最具体的元素(内部 div)向上传递到最不具体的元素(外部 div)。
事件捕获 (Capturing):正好相反,事件从最外层(外部 div)向下传递到最具体的元素(内部 div),就像老鹰从天而降捕捉猎物一样。
addEventListener 默认使用冒泡模式,只有设置了 { capture: true } 才会开启捕获模式。
