I've talked to several people recently who were surprised to learn that adding a touch event handler to your page (even if you do nothing in it) can have significant performance implications.  Here are the details, what you can do to minimize the issue, and what we're doing in Chrome to help solve it.

When a user touches and drags a web page, what should the browser do?  Most of the time the user expects to scroll the page, but if they happen to be touching on top of something like a map then they expect instead to pan the map and scrolling the page (even very briefly) will be wrong and jarring to the user.  The web page tells the browser that a touch shouldn't scroll the page by calling preventDefault on the TouchEvent.  But modern browsers work very hard to implement scrolling on a thread separate from the main thread that runs JavaScript (substantially improving responsiveness and smoothness of scrolling in practice).  This means that when a user does a touchscreen scroll gesture, the browser must dispatch a touchstart event to the page and WAIT to see if if preventDefault gets called on the event before scrolling is started.  Worse, in general, every time the finger moves, the browser must dispatch a touchmove event and wait to see if preventDefault gets called before updating the scroll position.  At any point in time, the main thread may be blocked for a non-trivial length of time on any number of things, including long running JavaScript, loading a page, or performing layout.  When touch events are involved, each of these things has the opportunity to cause scroll jank (uneven, stuttering scrolling) or delays (scrolling taking longer, perhaps MUCH longer, to start).

Different browsers have a variety of different optimizations to improve the situation.  Chrome initially had a very simple optimization: if there are no touch handlers on the page at all, then we can avoid sending touch events entirely (and so avoid blocking scrolling).  This means that adding a no-op touchstart handler anywhere to a page can trigger scroll performance problems.  You can see the impact of this in an exagerated example here: http://www.rbyers.net/janky-touch-scroll.html.

In M25, Chrome for Android got a new feature called "compositor thread hit testing" where the thread doing the scrolling is able to tell if there is a touch event handler at any given co-ordinate (this is non-trivial because the DOM is not multi-threaded, and so can only be accessed safely from the main thread).  This means that adding a touch event handler will only impact performance of scrolls which start on top of the element with the handler.  This feature was enabled on other Chrome platforms in M26.  I wrote a little test page to demonstrate the effect of this feature here: http://www.rbyers.net/scroll-hit-test.html.

Chrome for Android also has what they consider to be a hack: once scrolling has started, they will not allow the page to cancel scrolling. Instead of sending touchmove events throughout the scroll, a touchcancel is dispatched and then there are no further touch events for the scroll.  This avoids the problem with touchmove handlers causing jank, but does nothing to address the issue of touchstart handlers delaying scroll.  This behavior has been a source of compatibility issues, and was viewed by the team as a workaround until they could implement compositor thread hit-testing, and so it may be going away soon.  Mobile Safari has similar behavior in some  cases (scrolling the main document, and -webkit-overflow-scrolling: touch).

In order to completely solve this problem, we need to give the web page a way to tell the browser when to scroll and when not to scroll without relying on JavaScript.  Pointer events (https://dvcs.w3.org/hg/pointerevents/raw-file/tip/pointerEvents.html) was designed from the beginning to avoid this problem, which it does through the "touch-action" CSS property.  As soon as the user touches the page, the browser can decide whether that touch will result in a scroll or not by looking at the touch-action of the elements under the finger (and our compositor-thread hit testing code is written to facilitate this sort of use too).  To me this is the most significant design point of pointer events (it dictates much of the rest of the design), and it's  one of the main reasons I believe we should implement pointer events in Chrome.

Until we have touch-action (or something like it), the main thing you can do is to avoid putting touch event handlers on parts of the page the user will expect to be able to scroll, especially the document.  Smooth scrolling and touch event handling are often at odds, so ideally your page will be cleanly separated into regions that handle touch events, and regions where scrolling is possible.  When this isn't possible, it's even more important to try to ensure that your page is fast enough (even on slow hardware) that it rarely does anything that takes longer than 16ms (even the occasional dropped frame while scrolling is perceived as jank).  Chrome's frame view in the timeline panel is an easy way to visualize this.

By the way, none of what I've described here is really fundamentally limited to touch.  The same issue arrives in practice with the mousewheel event.  But the psychological connection between a user's finger and the page is much stronger when they see their finger directly on top of what they're manipulating, so performance is more important here.

#javascript   #touchscreen   #chrome  
Shared publiclyView activity
Related Collections