This is not my original, organized information to facilitate learning and accumulation of knowledge

Mobile terminal 300ms click delay

Story: Early 2007. In the run-up to the launch of apple’s first iPhone, the company had a problem: Its website was designed for devices with larger screens. So Apple’s engineers made conventions to deal with the small screen of the iPhone when browsing desktop sites.

The most famous of these is the double tap to Zoom, which is the main reason for the 300-millisecond delay.

Double zoom, as the name suggests, involves two quick finger taps on the screen, and the Safari browser that comes with iOS scales the web to its original proportions. So what does this have to do with the 300 millisecond delay? Consider this scenario. The user clicks on a link in iOS Safari. Since the user can double click to zoom or double click to scroll, once the user clicks the screen, the browser can’t immediately determine whether the user actually wants to open the link or whether the user wants to double click. So iOS Safari waits 300 milliseconds to see if the user has clicked on the screen again. Given the success of the iPhone, other mobile browsers have copied most of the conventions of the iPhone’s Safari browser, including double-clicking to zoom, which is now available on almost all mobile browsers. Before, when people just touched the page of mobile terminal, they would not care about the 300ms delay. However, now the interface of touch terminal is mushroomed and users have higher requirements for experience. The lag caused by 300ms gradually becomes unacceptable.

That said, mobile browsers have some default behaviors, like double click to zoom, double click to scroll. These behaviors, especially double click zooming, are primarily designed for the mobile browsing experience of desktop websites. When users perform operations on the page, mobile browsers will preferentially determine whether users want to trigger the default behavior.

Important: because there will be a double click zoom operation on the mobile terminal, so the browser will wait 300ms after click to see whether the user has the next click, that is, whether the operation is double click.

Browser developer’s solution

Option 1: Disable zooming when the HTML document header contains the following meta tags:

<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,maximum-scale=1">
Copy the code

Indicates that the page is not scalable, so the double click zoom function is meaningless, and the browser can disable the default double click zoom behavior and remove the 300ms click delay.

Disadvantages:We had to disable zooming completely to get rid of click delay, but it wasn’t our intention to disable zooming completely, we just wanted to disable the default double click zooming behavior so that we didn’t have to wait 300ms to determine whether the current action was a double click. But more often than not, we want pages to be zoomed in with two fingers, for example to zoom in on an image or a small piece of text.



Option 2: Change the default viewport width


Initially, in order for desktop sites to display properly in mobile browsers, the default viewport width of mobile browsers is not equal to the width of the device browser viewport, but larger than the device browser viewport width, usually 980px. You can set the viewport width to the device width by using the following labels.

<meta name="viewport" content="width=device-width">
Copy the code

Because double click zoom is mainly used to improve the desktop site in the mobile browsing experience, and with the popularity of responsive design, many sites have been adapted and optimized for mobile sitting, at this time there is no need to double click zoom, if you can identify a website is responsive website, Mobile browsers can automatically disable the default double click zoom behavior and remove the 300ms click delay. If the meta tags are set, the browser can assume that the site has been adapted and optimized for mobile, without the need to double click to zoom. The advantage of this scheme over scheme 1 is that it does not completely disable zooming, but just disables the browser’s default double-click zooming, but users can still zoom through the two-finger zooming operation.

Solution 3: CSS touch-Action


Touch-action is the CSS property. This property specifies the default behavior of the user agent (that is, the browser) that can be triggered on the corresponding element. If the value of this attribute is set to touch-action: None, it means that actions on this element do not trigger any default behavior by the user agent, and no 300ms delay is required.

Existing solutions

Pointer events are currently not supported by most browsers except Internet Explorer. There are JS libraries that allow us to use pointer events in advance, for example

  • Google 的 Polymer
  • Microsoft’s HandJS
  • @ Rich – Harris Points

However, instead of pointer events, we are now concerned with the CSS property touch-Action related to the 300ms delay. Since most browsers other than Internet Explorer do not support this new CSS property, polyfills of pointer events must somehow emulate support for this property. One option is for JS to request parsing of all stylesheets, and the other option is for touch-action to be an attribute of the HTML tag.

FastClick is a lightweight library developed by FT Labs to solve the 300 millisecond click delay problem in mobile browsers. The implementation principle of FastClick is that when the Touchend event is detected, the DOM custom event will immediately simulate a click event and block the browser click event after 300ms.

Second, click penetration problem

After talking about the 300ms delay of mobile terminal click, we also have to mention the problem of mobile terminal click penetration. One might think, since the click has a 300ms delay, wouldn’t we just listen for the TouchStart event for the touch screen? There are two drawbacks to using touchStart instead of the click event. First, touchStart is triggered when a finger touches the screen. Sometimes the user just wants to swipe the screen, but the TouchStart event is triggered, which is not what we want. Second: using the TouchStart event may cause click penetration in some scenarios.

What is clickthrough? Suppose there are two elements A and B on the page. B is on top of A. We registered a callback function on the touchStart event of the B element, which hides the B element. We find that when we click on the B element, the B element is hidden, and then the A element fires the click event.

This is because in mobile browsers, events are executed in the order TouchStart > TouchEnd > Click. The click event has A 300ms delay. After the TouchStart event hides the B element, the browser fires the click event 300ms later, but the B element is missing, so the event is sent to the A element. If the A element is A link, the page will jump by accident.

Note: The order in which browser events are triggered

Touchstart –> mouseover(not implemented in some browsers) –> Mousemove –> mouseDown –> mouseup –> Click –> TouchEnd

Touch events, commonly used for touchStart, touchmove, touchend three. And then there’s ‘TouchCancel.’ Note that there is no TAP event in the native event. The following explains how the TAP event is generated.

The event description is as follows:

The event describe trigger
touchstart Begin to touch Triggered immediately when a finger touches the screen
touchmove Move or drag Depends on the system and browser
touchend Touch the end of the Start as soon as your finger leaves the screen

While Touch events are generally triggered by fingers, there will also be multi-touch, drag direction and other situations. Several important parameters are listed as follows:

parameter meaning
touches A list of each finger on the screen
targetTouches Similar to Touches, it filters out fingers on the same node
changedTouches A list of information for each finger responding to the current event

The code is obtained as follows:

elemenrRef.addEventListener('touchstart'.function(e) {   
    console.log(e.touches, e.targetTouches, e.changedTouches);}
);Copy the code

The finger triggers the touch event as follows:

Touchstart --> mouseover(not implemented in some browsers) --> Mousemove --> mouseDown --> mouseup --> Click --> TouchEndCopy the code

From there, we can record the start of touch on the onTouchStart event and the end of touch on the onTouchEnd event. With these parameters, it is easy to calculate the time of the nether click and the information related to the click penetration, including the coordinate situation of the response.

Phenomenon:

1) Click through problem: click the close button on the mask, and it will trigger the click event of the element below the button after the mask disappears.

The close button of the mask layer is bound to the touch event, while the element below the button is bound to the click event. After the touch event is triggered, the mask disappears. After 300ms, the click event fire and event target of this point are naturally the elements below the button, because the button disappears with the mask





2) Cross-page click penetration problem: if the button happens to be under an A tag with href attribute, then the page will jump

Since the A tag jump is triggered by the click event by default, the principle is exactly the same as above

3) Another cross-page click penetration problem: there is no mask this time, click the button in the page directly to jump to a new page, and then find that the click event of the corresponding element in the new page is triggered

And cover layer of truth, js jump of control page logic if it is binding on the touch events, and a new page in the corresponding position of the element binding is the click event, and jump page completed within 300 ms, and satisfy the three conditions, will appear this kind of circumstance to segment and the fourth, but the probability is very low, That is, the corresponding element in the new page happens to be a tag, and then the continuous jump occurs… And so on and so forth, it’s click penetration

Solution:

  1. Only touch

    The simplest solution perfectly solves the click penetration problem

    Replace all clicks within the page with touch events (touchStart, ‘touchend’, ‘tap’),

    Special attention is required

    A tag, the href of a tag is also click, which needs to be removed and replaced with js control jump, or directly changed to span + tap control jump. If you’re not too demanding and don’t care if you slide or slide in to trigger an event, span + Touchend will do, because tap requires third-party libraries

    There’s nothing wrong with not using the A tag. You don’t need to consider SEO in mobile app development. Even if you use the A tag, you will usually remove all default styles

  2. Just click

    The very

    Any custom interaction within the page adds 300 milliseconds of latency, which is slow to think about

    If you don’t use touch, you won’t be able to trigger click 300ms after touch.

    Strongly not recommended

    It’s always good to be quick

  3. Mask hiding is delayed 350ms after tap

    The modification is minimal, the disadvantage is that the hiding mask is slow, 350ms can still feel the slow

    Only the mask needs to be processed, the change is very small, if the requirements are not high, it is easier to use this

  4. pointer-events

    It’s cumbersome and flawed,

    Not recommended

    After the mask is hidden, add pointer-events: None to the element below the button; Style, let click pass through, remove the style after 350ms, restore the response

    The defect is that within 350ms after the mask disappears, the user can see that the element under the button does not respond to the light, which will definitely be found if the user has a fast hand speed

  5. Check in the event handler of the following element (with global flag)

    It’s a hassle,

    Not recommended:

    Global Flag records the location of the button click (coordinate point), determines the coordinate point of the event in the event handler of the following element, or if the same is the same, that damned click, rejects the response

    The above is just an idea, which has not been tested. If it is not possible, use the record timestamp to judge and wait for 350ms, which is similar to pointer-Events

  6. fastclick

    Nice solution, if you don’t mind loading a few extra kilobytes,

    Not recommended:

    The fastclick library is used to change all touch events in the page to click. It is not worth using the first method to solve the problem


Note: The code handling suggestions are as follows:


After a successful click, it is recommended to cancel all click events for the next 500ms.


Analyze click events and determine that if it is slow click penetration, cancel all click events; if it is fast click penetration, cancel click events within 50ms of touch events.


Other ideas (open source library FastClick) : Cancel the click event and use Touchend to simulate fast clicking behavior.


Why

The question is, when does the click event fire?

The browser waits about 300ms after touchend and fires the click event if there is no tap action. The reason the browser waits around 300ms is to determine if the user is a double tap, and it is not appropriate to trigger the click event during the double tap. You can see that the click event trigger represents the end of a round of touch events.

As mentioned above, there is no tap event in the native event, please refer to the classic Zepto.js handling of singleTap event (unfortunately, in some browsers, there is still a click penetration problem). It can be seen that the trigger time of the singleTap event — the singleTap event is triggered after the Touchend event response has no operation for 250ms. Therefore, click penetration is easy to understand. Within this 300ms, because the upper element is hidden or gone, the DOM element in the same position fires the Click event (or the focus event in the case of input) due to the lag of the click event. In the code, it feels like the target has drifted.

How to handle click penetration

1. PreventDefault () when touchStart event is triggered at the start of touch. No doubt, it is easy to think of this point, and also fundamentally solves the problem. However, it has a major drawback, or introduces a major drawback, that DOM elements in a page can no longer scroll. This method obviously does not meet our needs, but this idea can give us more inspiration, such as iscroll only allows horizontal scrolling implementation, the related implementation is not listed here.

2. PreventDefault () when the touchEnd event is triggered at the end of touch. It seems that there is no problem, but unfortunately not all browsers support it.

3. Disable page zooming by setting the meta tag, you can disable page zooming. Some browsers do not need to wait 300ms, which causes click penetration. Click events still fire, but relatively quickly, so click events can in some sense replace click events at the expense of a few users who still fire slowly.

<meta name="viewport" content="width=device-width, user-scalable=no">Copy the code

Chromiun on mobile and iOS 9.3+ can use CSS properties to prevent double-clicking on elements and cancel the click penetration delay:

html { -ms-touch-action: manipulation; touch-action: manipulation; }Copy the code

4. Although CSS3 methods mainly talk about events, it is necessary to introduce a CSS3 attribute — pointer-Events.

pointer-events:  auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit;Copy the code

The pointer-Events attribute has many values. The most useful ones are Auto and None. The other attributes serve SVG.

So mobile development can still work.

attribute meaning
auto By default, mouse or touchscreen events do not penetrate the current layer
none The element is no longer the target, and the listening element becomes the underlying element (if the child element is set to auto, clicking on the child element continues to listen for events).

5. Handle Click events — Touch to Click’s best bet is to address the source of the Click event. Use JS to determine the nether click and prevent the click from penetrating. This is obviously possible, but the downside is that you need to be careful not to prevent clickthrough and cause native HTML elements (links, checkboxes, checkboxes) to not work properly.

With the aforementioned touches, targetTouches, and changedTouches parameters, we can create a test page that measures how long touches have been created and how many have responded.

preventDefault() Click penetration time Click through area
touchstart touchend Zoom page Disable page zooming Zoom page Disable page zooming
Safari Mobile iOS 5.1.1 Yes Yes 370ms after end 370msafter end touchstart touchstart
Safari Mobile iOS also 6.1.3 Yes Yes 370ms after end 370msafter end touchstart touchstart
Safari Mobile iOS 7.1.1 Yes Yes 370ms after end 370msafter end touchstart touchstart
Android 2.3.7 Yes No 410ms after end 410msafter end touchstart touchstart
Android 4.0.4 Yes No 300ms after end 10ms after end touchstart touchstart
Android 4.1.2 Yes No 300ms after end 300msafter end touchstart touchstart
Android 4.2.2 Yes No 300ms after start 10ms after end touchstart touchend
IE10 Windows Phone 8 No No 310ms after end 10ms after end touchend touchend
Blackberry 10 Yes Yes 260ms after end 10ms after end touchstart touchstart
Chrome for iOS Yes Yes 360ms after end 360msafter end touchstart touchstart
Chrome for Android Yes Yes 300ms after start 10ms after end touchstart touchend
Firefox for Android Yes No 300ms after end 10ms after end touchstart touchend

Click through is affected by browser and whether the page is zoomed. 2. There are two cases of click penetration: the fast case has 10ms and the slow case has 300ms 3. Call preventDefault() on touchend time to prevent clickthrough in most cases