深入理解 addEventListener

语法一: target.addEventListener(type, listener, useCapture)

参数一:事件类型,比如 click、mouseenter、drag等。

参数二:事件被触发时的回调函数。

参数三:useCapture: 默认值为false,表示在冒泡阶段处理事件。 如果为true,则在捕获阶段处理事件。



语法二:target.addEventListener(type, listener, options)

options对象:

useCapture: 默认值为false,事件将在冒泡阶段触发。 如果为true,事件将在捕获阶段触发。

once: 表示 listener 在添加之后是否最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。

passive: 设置为 true 时,表示 listener 永远不会调用 preventDefault()。使用 passive 可改善的滚屏性能。(根据标准,passive的值默认为false,最新的chrome和firefox已经默认将passive设置为true。)



为什么会有 passive 的出现??-- addEventListener使用 passive 可改善的滚屏性能

移动端的一些事件比如 touchstart 、 touchmove 、 touchend 、 touchcancel 等,如果在这些事件中阻止默认行为,页面会被禁止滚动或缩放。


而浏览器无法事先知道一个监听器是否会禁止默认行为,要等监听器执行之后,才会去执行默认行为。而监听器的执行是要耗时的,如果在 event.preventDefault() 之前耗时了 2秒 ,这样就会导致页面卡顿。


为了提升此种场景下的滚动体验,我们需要有一个参数来告诉浏览器,我的事件监听器中不会有 event.preventDefault() ,你可以不用等监听器执行完毕,请尽情的滚动吧。所以有了 passive 属性。为了兼容之前的 useCapture ,把最后一个参数改成了一个对象 options。


所以在绑定移动端相关的 touch 和滚动事件时,尽可能使用 { passive: true } 来提升性能和体验,避免出现页面卡顿。


然而,不是所有的浏览器都支持 passive 特性,不支持 passive 特性的浏览器会把最后一个参数当作 useCapture ,所以需要这段精妙的代码判断是否支持 passive 特性:

// Test via a getter in the options object to see 
// if the passive property is accessed
var supportsPassive = false;
try {
  var opts = Object.defineProperty({}, 'passive', {
    get: function() {
      supportsPassive = true;
    }
  });
  window.addEventListener("test", null, opts);
} catch (e) {}


// Use our detect's results. 
// passive applied if supported, capture will be false either way.
elem.addEventListener(
  'touchstart',
  fn,
  supportsPassive ? { passive: true } : false
);

通过 Object.defineProperty 设置一个 passive 的 get 访问器,添加一个 test 的事件,当浏览器支持的时候会调用 get 访问器,在 get 访问器中设置 supportsPassive。


在Angular框架中,Angular CDK 早已在 @angular/cdk/platform 模块提供了normalizePassiveListenerOptions({passive: true}) 供我们解决兼容性的问题,核心代码如下:

/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

/** Cached result of whether the user's browser supports passive event listeners. */
let supportsPassiveEvents: boolean;

/**
 * Checks whether the user's browser supports passive event listeners.
 * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
 */
export function supportsPassiveEventListeners(): boolean {
  if (supportsPassiveEvents == null && typeof window !== 'undefined') {
    try {
      window.addEventListener(
        'test',
        null!,
        Object.defineProperty({}, 'passive', {
          get: () => (supportsPassiveEvents = true),
        }),
      );
   } finally {
      supportsPassiveEvents = supportsPassiveEvents || false;
    }
  }

  return supportsPassiveEvents;
}

/**
 * Normalizes an `AddEventListener` object to something that can be passed
 * to `addEventListener` on any browser, no matter whether it supports the
 * `options` parameter.
 * @param options Object to be normalized.
 */
export function normalizePassiveListenerOptions(
  options: AddEventListenerOptions,
): AddEventListenerOptions | boolean {
  return supportsPassiveEventListeners() ? options : !!options.capture;
}

添加绑定事件支持 passive 参数的相关Issue:https://github.com/angular/angular/issues/8866

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值