我们知道,滚动响应能力对用户与移动网站的互动至关重要,但触摸事件监听器通常会导致严重的滚动性能问题。Chrome 一直在通过允许触摸事件监听器为被动(将 {passive: true}
选项传递给 addEventListener()
)并提供指针事件 API 来解决此问题。这些功能非常适合将新内容引入到不会阻止滚动的模型中,但开发者有时会发现它们难以理解和采用。
我们认为,Web 应该默认快速,开发者无需了解浏览器行为的深奥细节。在 Chrome 56 中,如果在大多数情况下,被动触摸监听器与开发者的意图相符,我们会默认将触摸监听器设为被动。我们相信,通过这样做,我们可以大幅提升用户体验,同时对网站的影响降到最低。
在极少数情况下,此更改可能会导致意外滚动。通常,只需将 touch-action: none 样式应用于不应发生滚动的元素,即可轻松解决此问题。请继续阅读,详细了解相关信息、如何确定您是否受到影响,以及您可以采取哪些措施。
背景知识:可取消的事件会降低网页速度
如果您在 touchstart
或第一个 touchmove
事件中调用 preventDefault(),则会阻止滚动。问题在于,大多数情况下监听器不会调用 preventDefault()
,但浏览器需要等待事件完成才能确定这一点。开发者定义的“被动事件监听器”可解决此问题。当您在事件处理程序中添加触摸事件并将 {passive: true}
对象作为第三个参数时,您将告知浏览器 touchstart
监听器不会调用 preventDefault()
,并且浏览器可以安全地执行滚动,而不会阻塞监听器。例如:
window.addEventListener("touchstart", func, {passive: true} );
干预
我们的主要目标是缩短用户触摸屏幕后更新显示内容所需的时间。为了了解 touchstart 和 touchmove 的使用情况,我们添加了一些指标来确定滚动阻塞行为的发生频率。
我们查看了发送到根目标(窗口、文档或正文)的可取消触摸事件的百分比,并确定其中约 80% 的监听器在概念上是被动的,但并未以这种方式注册。鉴于此问题的规模,我们发现了一个绝佳的机会,可以通过将这些事件自动设为“被动”来改善滚动,而无需开发者执行任何操作。
因此,我们将干预定义为:如果 touchstart 或 touchmove 监听器的目标是 window
、document
或 body
,我们将 passive
默认为 true
。这意味着,以下代码
window.addEventListener("touchstart", func);
等效于:
window.addEventListener("touchstart", func, {passive: true} );
现在,系统会忽略监听器内对 preventDefault()
的调用。
下图显示了前 1% 滚动操作所需的时间,从用户轻触屏幕开始滚动到显示屏更新的时间。这些数据适用于 Android 版 Chrome 中的所有网站。在启用干预措施之前,1% 的滚动操作只需要 400 毫秒多一点的时间。现在,在 Chrome 56 Beta 版中,该时间已缩短至 250 毫秒左右,缩短了约 38%。未来,我们希望将“passive true”设为 所有 touchstart
和 touchmove
监听器的默认值,将此时间缩短到 50 毫秒以下。
中断和指南
在大多数情况下,不会出现任何中断。但是,如果确实发生了断点,最常见的症状是,在您不希望滚动时发生滚动。在极少数情况下,开发者可能还会注意到意外的点击事件(当 touchend
监听器中缺少 preventDefault()
时)。
在 Chrome 56 及更高版本中,当您在干预处于有效状态的事件中调用 preventDefault()
时,DevTools 会记录一条警告。
touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
您的应用可以通过检查调用 preventDefault
是否通过 defaultPrevented
属性产生了任何影响,来确定它是否可能会在实际环境中遇到此问题。
我们发现,只要尽可能应用 touch-action CSS 属性,就可以相对轻松地修复大多数受影响的网页。如果您想阻止在元素内进行所有浏览器滚动和缩放,请对其应用 touch-action: none
。如果您使用的是横向轮播界面,不妨考虑对其应用 touch-action: pan-y pinch-zoom
,以便用户仍能照常垂直滚动和缩放。在桌面版 Edge 等支持指针事件而非触摸事件的浏览器中,正确应用 touch-action 已是必不可少的。对于不支持 touch-action 的移动 Safari 和旧版移动浏览器,您的触摸监听器必须继续调用 preventDefault
,即使 Chrome 会忽略它也是如此。
在更复杂的情况下,可能还需要依赖以下任一方法:
- 如果您的
touchstart
监听器调用preventDefault()
,请确保从关联的 touchend 监听器中也调用 preventDefault(),以继续抑制生成点击事件和其他默认点按行为。 - 最后(不建议)将
{passive: false}
传递给 addEventListener() 以替换默认行为。请注意,您必须进行功能检测,以确定用户代理是否支持 EventListenerOptions。
总结
在 Chrome 56 中,许多网站的滚动启动速度明显更快。这是大多数开发者会注意到的唯一影响。在某些情况下,开发者可能会注意到意外滚动。
虽然对于移动 Safari 仍需要这样做,但网站不应依赖于在 touchstart
和 touchmove
监听器中调用 preventDefault()
,因为 Chrome 不再保证会执行此操作。开发者应在应停用滚动和缩放的元素上应用 touch-action
CSS 属性,以便在发生任何触摸事件之前通知浏览器。如需抑制点按的默认行为(例如生成点击事件),请在 touchend
监听器内调用 preventDefault()
。