【开发笔记】JavaScript EventListener 正确的使用姿势
addEventListener(type, listener) 虽然是简单的 Web API 调用,但是如果没有正确地去意识调用所填入的功能形式,往往会因为记忆体参照的目标不同,有可能导致事件监听无法移除,进而持续地消耗 runtime 资源,甚至产生奇怪的运行结果
留意事件监听回调方法的配置
最常犯的错误,就是忽略了 anonymous function 其实是各自独立的记忆体参照。举例来说,如果我们在回圈内调用 addEventListener(type, listener) ,并且填入 anonymous function 作为 listener。如此一来,每当回圈执行 addEventListener(type, listener) 时,就会持续地创建新的记忆体参照给 anonymous function(参考代码如下)。
const element = document.getElementsByTagName("aTagName");
let i = 0;
while (i < 10) {
i++;
element.addEventListener("keypress",
(event) => {
if (event.key === "Enter") {
event.preventDefault();
// do something here...
}
},
false)
}
至于解决的方法,只要在回圈外先定义宣告好方法就可以了(参考代码如下)。
function processEvent(event) {
if (event.key === "Enter") {
event.preventDefault();
// do something here...
}
}
const element = document.getElementsByTagName("aTagName");
let i = 0;
while (i < 10) {
i++;
element.addEventListener("keypress", processEvent, false);
}
留意事件监听移除的配置必须与新增的配置相同
由于事件监听移除的配置必须与新增的配置相同,所以作为 Listener 的方法需要一个变量参照(参考代码如下)。
const listener = function processEvent(event) {
if (event.key === "Enter") {
event.preventDefault();
// do something here...
}
}
element.addEventListener("keypress", processEvent, false);
// 事件监听移除的配置必须与新增的配置相同
element.removeEventListener("keypress", processEvent, false);
Capture Phase 和 Bubbling Phase
Capture Phase 和 Bubbling Phase 是两种相反的事件传播顺序。Capture Phase 是从 Window
开始,依序 Document
-> <html>
-> <body>
-> .... -> 监听的元素
。反之,Bubbling Phase 是从监听的元素
开始,由内向外传播 ... -> <body>
-> <html>
-> Document
-> Window
。
默认的情况是采用 Bubbling Phase,也就是 addEventListener("keypress", processEvent) 或 addEventListener("keypress", processEvent, false)。采用 Capture Phase 监听则需要配置成 addEventListener("keypress", processEvent, false) 或 addEventListener("keypress", processEvent, { useCapture: true })。
同场加映 passive 和 once
addEventListener(type, listener, option) 在 option 处除了可以配置 useCapture,还有 passive 和 once 可以设定控制。passive 会影响画面渲染时会不会被 listener 给阻断。默认状态下 { passive: true },也就是画面渲染时不会被 listener 给阻断。
而 once 比较容易理解,顾名思义,当设置 { once: true} 时,可以强制让该事件只会触发监听方法一次。
事件属性中的 target 和 currentTarget
最后就是要小心事件属性的 target 和 currentTarget 有可能不同。current target 表示的是 addEventListener 绑定的元素,而 target 则是引发该事件的元素。举例来说,当使用者点击页面上某个连结时,target 指的是监听事件的元素 <a>
,而 currentTarget 则是 addEventListener 所绑定的元素(参考代码如下)。
const anchor = document.querySelectorAll( 'a' )[0];
const div = document.querySelectorAll( 'div' )[0];
div.addEventListener('keypress', function(event) {
console.log(event.target, event.currentTarget );
// event.target 是 anchor,event.currentTarget 是 div
event.preventDefault();
});