The cause of

Fastclick.js is introduced in the corporate framework of mobile projects by default. Because of business requirements, the select component in Ant-Design was also introduced. As a result, on the iOS side, the select component needs to be double-clicked to pop up options.

Digging deeper into the problem, it turns out that FastClick is causing the problem.

why

DOM event triggering order

First, we need to know the triggering sequence of click, mouse event and touch event:

onTouchStart => (onTouchmove) = > onTouchEnd= > mousedown= > (mousemove) = > mouseup= > click
Copy the code

Fastclick mechanism

By looking at the FastClick source code:

  1. Fastclick inonTouchEndCall theevent.preventDefault()Blocking the default event (will block subsequent eventsmouse,clickEvent trigger).
  2. And create and trigger customclickEvents (for nativeselectThe element firesmousedownEvents)

From the above analysis, the Mouse event is not emitted if the element is not a native SELECT component.

For onTouchStart and onTouchEnd to prevent the default event by calling event.preventDefault(), see Touch event — MDN

How does ant-Design Select trigger option pop-ups

By looking at the rC-SELECT source code used by Ant-Design, we can see that it emulates the native SELECT, using the mousedown event to trigger the popup option, but it does not use the SELECT element internally. Instead, it emulates the div element:

const onInternalMouseDown: React.MouseEventHandler<HTMLDivElement> = (event, ... restArgs) = > {
    // xxxx
    if (onMouseDown) {
        onMouseDown(event, ...restArgs);
    }
};
/ / the dom structure

return (
  <div
      className={mergedClassName}
      {. domProps}
      ref={containerRef}
      onMouseDown={onInternalMouseDown}
      onKeyDown={onInternalKeyDown}
      onKeyUp={onInternalKeyUp}
      onFocus={onContainerFocus}
      onBlur={onContainerBlur}
      >{mockFocused && ! mergedOpen && (<span
              style={{
                  width: 0.height: 0.display: 'flex',
                  overflow: 'hidden',
                  opacity: 0,}}aria-live="polite"
			  >
              {/* Merge into one string to make screen reader work as expect */}
			  {`${mergedRawValue.join(', ')}`}
			  </span>
	  )}
      {selectorNode}
      {arrowNode}
      {clearNode}
  </div>
)
Copy the code

The component implementation is in the react-component/select file address: github.com/react-compo…

Fastclick does not recognize the component as a native SELECT, causing click events to be dispatched instead of mousedown events, resulting in clicks that go unanswered.

Why can double click trigger

Through a closer look at the source code, we can see that FastClick has a special processing for double click events. When the double click delay is less than 250ms (fastclick default is the double click judgment time), when the double click event will trigger fastclick processing. First, in onTouchStart:

FastClick.prototype.onTouchStart = function(event) {
    // xxx
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { event.preventDefault(); }}Copy the code

Although event.preventDefault() is called in onTouchStart, it doesn’t prevent subsequent events from happening. In order to allow faster scrolling on mobile, the browser defaults to onTouchStart with passive: true, so calling event.preventDefault() will be ignored (Chrome 56+).

Making touch scrolling fast by default

In the subsequent onTouchEnd, double-click is also judged:

FastClick.prototype.onTouchEnd = function(event) {
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
        this.cancelNextClick = true;
        return true;
    }
    // xxx
}

Copy the code

OnTouchEnd prevents subsequent custom events from firing by returning true, causing subsequent native MouseDown events to fire, which in turn triggers the Select onMouseDown event by Ant-Design.

How to solve

Because the project does not need to be compatible with older browsers, and

is already set:

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
Copy the code

You don’t need FastClick for compatibility in your project, so you end up killing fastClick

5 way Prevent 300ms Click delay Mobile Devices