首页
文章
隐私
  • 繁體中文
  • 简体中文
首页
文章
隐私
  • 繁體中文
  • 简体中文

【开发笔记】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();
});