Original text: developers.google.com/web/updates…

We know that scroll sensitivity is critical to mobile user engagement with web sites, but touch event listeners often cause serious scroll performance issues. Chrome already handles this by allowing touch event listeners to be passive(passing {passive: true} to addEventListener() and) and by strictly managing the Pointer API. These are nice features that allow new content to enter the model without blocking the slide, but developers sometimes find this hard to understand and adopt.

We believe that web pages should be naturally fast, rather than requiring developers to understand arcane details of browser behavior. In Chrome 56, we set the Touch listener to passive by default to attract developers’ attention. We believe that by doing this, we can greatly improve the user experience and minimize negative experiences on the site.

In rare cases, this adjustment may cause unintended rolling. This can often be done simply by applying the touch-action: None style to elements that should not scroll. Read on for more details on how to know if you are affected and what to do if so.

Background: Cancelable events make your page slower

View the comparison video





If the preventDefault() method is called on the TouchStart or first TouchMove event, then you will prevent the scrolling event. The problem is that most listeners won’t call the preventDefault() method, but browsers need to wait for the event to end to make sure it does. The developer’s definition of “passive Event Listeners “solves this problem. When you add {passive: true} to a touch event as the third argument to the event handler, you tell the browser that the touchStart event will not call preventDefault(), and the browser can safely scroll without blocking. Such as:

window.addEventListener("touchstart", func, {passive: true});Copy the code

interference

Our primary motivation is to reduce the time it takes to update the display after the user touches the screen. To understand the use of TouchStart and TouchMove, we added metrics to figure out how often scrolling blocking behavior occurs.

We looked at the percentage of untouchable events sent to the root target (window,document, or body) and figured out that about 80% of event listeners are theoretically passive, but we didn’t intentionally make them that way. Given the scale of the problem, we realized that there was a huge opportunity to improve scrolling by making these events automatically “passive” rather than through the developer.

This drives us to define the following interference rule: If the target of the TouchStart or TouchMove listener is window, Document, or body, we set passive to true by default. This means code like this:

window.addEventListener("touchstart".func);Copy the code

Becomes equivalent to:

window.addEventListener("touchstart", func, {passive: true} );Copy the code

Calls to preventDefault() inside listeners will now be ignored.

The chart below shows the time taken for the top 1% scrolling action, representing the time a user swiped from touching the screen until the display update was complete. This data is for all web pages running in Chrome for Android. Before this intervention was enabled,1% scrolling cost a little over 400ms. But this has now dropped to a little over 250ms in Chrome 56 Beta; A drop of about 38%. In the future we hope to make all TouchStart and TouchMove listeners passive by default, which will reduce this time to 50ms.





Destructiveness and guidance

In a large number of use cases, no destructive behavior was observed. But when the unexpected happens, the most common thing that happens is when you don’t want to roll, you roll. In rare cases, developers are also finding unexpected click events (when preventDefault() is missing in the Touch listener).

In Chrome 56 and later,DevTools will output a warning that you have called preventDefault() in the event when the intervention takes effect.

touch-passive.html:19 
Unable to preventDefault inside passive event listener due to target being treated as passive. 
See https://www.chromestatus.com/features/5093566007214080Copy the code

Your application can check to see if it’s going to step on the thunder somewhere. This check checks whether calling preventDefault has any impact through the DefaultSpoiled property.

We have found that a large number of affected pages can be fixed with the touch-ActionCSS property. If you want to block all browser scrolling and zooming on the element, add touch-action: None. If you have a horizontal running lantern and consider applying touch-Action: Pan-y pinch-Zoom to it, then the user will be able to scroll and zoom vertically as usual. On browsers that support Pointer Events and non-Touch Events, such as the desktop version of Edge, the proper application of Touch-Action has become essential. For Safari on mobile and older mobile browsers that don’t support touch-Action, your Touch listener must continue to call the preventDefault method, even if it’s been ignored by Chrome.

In more complex cases, the following approach may be relied on:

  • If the TouchStart listener calls preventDefault(), ensure that the preventDefault() method is also called in the corresponding Touchend listener to ensure that the click event and other default click actions continue to be prevented.

  • The least (and not recommended) method is to explicitly pass {passive: false} into addEventListener() to override the default browser behavior. Note that if the browser supports EventListenerOptions, then you need to do feature checking.

conclusion

Chrome 56 has already seen a significant improvement in scrolling on many websites. The biggest impact is that most developers will be aware of the results of this change. In some cases, developers may notice unexpected scrolling.

Although this is still necessary for Safari on mobile, websites should not rely on calling preventDefault() on touchStart and TouchMove listeners. This is no longer supported or advocated in Chrome. Developers should add touch-actionCSS properties to elements that require scrolling and zooming to be disabled to notify the browser of any touch events before they occur. To prevent tap(like a click event) from behaving by default, call preventDefault() in the Touchend listener.