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:
- Fastclick in
onTouchEnd
Call theevent.preventDefault()
Blocking the default event (will block subsequent eventsmouse
,click
Event trigger). - And create and trigger custom
click
Events (for nativeselect
The element firesmousedown
Events)
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
<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