本文主要介绍addEventListener()
方法相关的事件操作内容。
DOM 节点的事件操作(监听和触发),都定义在EventTarget
接口。所有节点对象都部署了这个接口。
该接口主要提供三个实例方法。
addEventListener()
:绑定事件的监听函数removeEventListener()
:移除事件的监听函数dispatchEvent()
:触发事件来源
本文作为一篇学习笔记,大部分内容来源于网道。
在讲解具体的实例方法之前,先看一下事件模型。
一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播按照顺序分成三个阶段。
graph TB
capture["捕获阶段(capture phase) \n 从 window 对象传导到目标节点,即上层传到底层"] --> target["目标阶段(target phase) \n 在目标节点上触发"] --> bubble["冒泡阶段(bubble phase) \n 从目标节点传导回 window 对象,即从底层传回上层"]
这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
注意,浏览器总是假定click
事件的目标节点,就是点击位置嵌套最深的那个节点。
事件传播的最上层对象是window
,接着依次是document
,html
(document.documentElement
)和body
(document.body
)。
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方式叫做事件的代理(delegation)。
jsvar ul = document.querySelector('ul');
ul.addEventListener('click', (event) => {
// 判断点击的元素是否是 li 元素,如果是则会进行相应的操作
if (event.target.tagName.toLowerCase() === 'li') {
// do something
}
});
上面代码中,click
事件的监听函数定义在<ul>
节点,但是实际上,它处理的是子节点<li>
的click
事件。这样做的好处是,只要定义一个监听函数,就能处理多个子节点的事件,而不用在每个<li>
节点上定义监听函数。而且以后再添加子节点,监听函数依然有效。
如果希望事件到某个节点为止,不再传播,可以使用事件对象的stopPropagation
方法。
js// 事件传播到 p 元素后,就不再向下捕获了(不影响冒泡阶段)
p.addEventListener('click', function (event) {
event.stopPropagation();
}, true);
// 事件冒泡到 p 元素后,就不再向上冒泡了(不影响捕获阶段)
p.addEventListener('click', function (event) {
event.stopPropagation();
}, false);
上面代码中,stopPropagation
方法分别在捕获阶段和冒泡阶段,阻止了事件的传播。
但是,stopPropagation
方法只会阻止事件的传播,不会阻止该事件触发<p>
节点的其他click
事件的监听函数。也就是说,不是彻底取消click
事件。
jsp.addEventListener('click', function (event) {
event.stopPropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 会触发
console.log(2);
});
上面代码中,p
元素绑定了两个click
事件的监听函数。stopPropagation
方法只能阻止这个事件的传播,不能取消这个事件,因此,第二个监听函数会触发。输出结果会先是1,然后是2。
如果想要彻底取消该事件,不再触发后面所有click
的监听函数,可以使用stopImmediatePropagation
方法。
jsp.addEventListener('click', function (event) {
// 下面两句话不论先后顺序都会执行
event.stopImmediatePropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 不会被触发
console.log(2);
});
上面代码中,stopImmediatePropagation
方法可以彻底取消这个事件,使得后面绑定的所有click
监听函数都不再触发。所以,只会输出1,不会输出2。
target.addEventListener()
特点:
jstarget.addEventListener(type, listenerCb [, useCapture])
// 下面会产生两个监听函数,因为第三个参数不同
target.removeEventListener('click', listenerCb, false)
target.removeEventListener('click', listenerCb, true)
this
,指向当前事件所在的那个对象。方法入参:
type
:事件名称,大小写敏感。listenerCb
:监听函数。type
类型的事件发生时,会触发该监听函数。useCapture
:可选参数,布尔值;
false
,监听函数只在冒泡阶段和目标阶段被触发;true
,表示监听函数将在捕获阶段和目标阶段触发。注意
第二个参数除了可以是一个函数外,也可以是一个具有handleEvent
方法的对象,效果和监听函数相同。
jstargetEl.addEventListener('click', {
handleEvent: (e) => {
console.log('触发了点击事件')
}
})
第三个参数除了布尔值useCapture
,还可以是一个监听器配置对象,定制事件监听行为。该对象有以下属性。
capture
:布尔值,如果设为true
,表示监听函数在捕获阶段触发,默认为false
,在冒泡阶段触发。once
:布尔值,如果设为true
,表示监听函数执行一次就会自动移除,后面将不再监听该事件。该属性默认值为false
。passive
:布尔值,设为true
时,表示禁止监听函数调用preventDefault()
方法。如果调用了,浏览器将忽略这个要求,并在控制台输出一条警告。该属性默认值为false
。signal
:该属性的值为一个 AbortSignal 对象,为监听器设置了一个信号通道,用来在需要时发出信号,移除监听函数。target.removeEventListener()
该方法用来移除addEventListener()
方法添加的事件监听函数。
jstarget.addEventListener('click', listenerCb, false);
addEventListener()
方法完全一致。需要注意的是,该方法移除的监听函数,必须是addEventListener()
方法添加的同一个节点、同一个函数、同一个触发阶段,即三个参数完全一样才可以,否则是不生效的。
target.dispatchEvent()
该方法在当前节点上触发指定事件,从而触发监听函数的执行。
Event
对象的实例Event.preventDefault()
,则返回值为false
,否则为true
。jstarget.dispatchEvent(event)
jstarget.addEventListener('click', listenerCb, false);
const event = new Event('click');
target.dispatchEvent(event);
上面代码在当前节点触发了click
事件。
如果dispatchEvent()
方法的参数为空,或者不是一个有效的事件对象,将报错。
下面代码根据dispatchEvent()
方法的返回值,判断事件是否被取消了。
jsvar canceled = !target.dispatchEvent(event);
if (canceled) {
console.log('事件取消');
} else {
console.log('事件未取消');
}
需要注意的是: 对于不可取消的事件,调用preventDefault()
是没有任何效果的,可以通过使用Event.cancelable
来检查该事件是否支持取消。
本文作者:xhir
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!