scroll不同的是,scroll是监听页面或元素滚动,wheel是监听滚轮滚动。wheelEvent的默认操作是基于浏览器具体实现的,而且不一定会触发“滚动”这个行为,即使它触发了,wheeldelta*属性也不一定代表了滚动方向。so 不要依赖delta*属性来获取页面滚动方向,应该在scroll事件中检测目标的scrollLeftscrollTop的值来判断方向。

1
2
target.addEventListener("wheel", handleWheel);
target.onwheel = handleWheel;

这个事件继承了父接口 MouseEventUIEventEvent的属性,除此之外还有四个属性:

Constant Value Description
DOM_DELTA_PIXEL 0x00 delta 的值为 像素 级别.
DOM_DELTA_LINE 0x01 delta 的值为 行 级别.
DOM_DELTA_PAGE 0x02 delta 的值为 页 级别.

在 React 中,这个事件叫做onWheel

passive event listener

在做 wheel + ctrl 触发元素缩放时,在谷歌浏览器中会触发页面整体放大,理所当然的认为preventDefault()可以阻止这个默认行为,但是触发了一个报错:

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. Seehttps://www.chromestatus.com/features/6662647093133312

大致意思是,如果未另行指定,则在文档级目标(window.document,window.document.body 或 window)上注册的 wheel / mousewheel 事件将被视为被动的(passive),并且在此类事件中调用preventDefault()将会被忽略。

可以这样解决:

1
target.addEventListener("wheel", handleWheel, { passive: false });

一般来说,addEventListener的第三个参数是useCapture,表示是否在捕获阶段调用event handler,默认为false。但是其实也可以是一个options

1
2
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);

options的三个常用属性:

  • capture: 表示事件是否在捕获阶段调用event handler
  • once: 表示listener在添加之后最多只调用一次。如果是truelistener会在其被调用之后自动移除。
  • passive: 设置为true时,表示listener永远不会调用preventDefault()。如果仍然调用了preventDefault(),客户端将会忽略它并抛出一个控制台警告。

passive设置为true有什么好处呢!这里有个解释,意思是浏览器无法预先知道一个event handler会不会调用preventDefault(),它需要等handler的执行,才能知道是否执行默认行为,而handler执行是要耗时的,这时再去执行默认行为就会导致页面卡顿。而passive设置为true是告诉浏览器不会调用preventDefault(),不需要等待handler执行完就先按照默认行为进行下去,从而提高滑动或滚动事件的流畅度。

自我感觉意思就是当不需要阻止页面的默认行为时,设置{ passive: true }会对体验有很大的优化,而当真的不需要页面的默认行为时,设置{ passive: false }preventDefault()是没问题的。

这里还有一篇更加深层的文章:让页面滑动流畅得飞起的新特性:Passive Event Listeners