Source of problem

In an interview with Ali about fastClick solving the problem of 300ms click delay on iPhone, I was asked about zepto’s “click penetration” phenomenon and the specific reason. At that time, MY answer was not very good, mainly because I did not deeply study this reason. I just knew about this phenomenon and the problem and how to solve it. One day after the interview, I suddenly remembered it and decided to study it carefully.

In fact, there are a lot of articles are written, there are a lot of content I will not repeat, summarize the following points:

  1. The 300ms delay is due to the delay in processing click events caused by the browser deciding whether to single or double click

  2. The FastClick solution replaces the click event with TouchStart in combination with TouchMove and TouchEnd

  3. Zepto’s tap will “Pierce” the page because it responds to its own tap (also known as touch event) and does not intercept the original click event, causing the event to be executed twice, and the “Pierce” effect will appear when there is a mask layer. If you don’t understand, read this article about zepto’s breakdown

Years ago to explore

At that time, I had A big question that why the click event of the page below the mask layer would be triggered after the click was delayed. I clearly clicked the A button of the mask layer, but why the event of the B button of the page below would be executed. According to my initial idea, it should be continue to execute the A button event ah!! This is what I’m feeling right now





A face of meng

So I started to explore this question, I searched some information, basic didn’t tell the specific reason, maybe I turn on the right way, it is not found, in desperation, I can only read fastclick source to see why it did not appear this problem, and then saw sendClick code, the in the mind suddenly have a guess.

FastClick.prototype.sendClick = function(targetElement, event) {
    var clickEvent, touch;
    // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
    if (document.activeElement && document.activeElement ! == targetElement) {document.activeElement.blur();
    }
    touch = event.changedTouches[0];
    // Synthesise a click event, with an extra attribute so it can be tracked
    clickEvent = document.createEvent('MouseEvents');
    clickEvent.initMouseEvent(this.determineEventType(targetElement), true.true.window.1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false.false.false.false.0.null);
    clickEvent.forwardedTouchEvent = true;
    targetElement.dispatchEvent(clickEvent);
    };Copy the code

Note that initMouseEvent must have something to do with how mouseEvent is executed.

Then make

Then, began the New Year, the New Year period to enjoy the life, and did not touch the code and documents (good degenerate feeling……) “, coupled with my job-hopping gap and toss, a little stable after the year, and recently remembered the conjecture half explored before the year, began to continue to do, by the way to collect my heart, good into the state.

First guess — the click event is actually captured in the browser at first, with only the mouseEvent attribute, which is normally part of the console.log(event), and then the browser will produce the click time with HTML and JS. This then triggers the function we bound with JS.





The various properties of the event in general

With this assumption in mind, I started scrolling through mozilla and W3C documentation to learn more about mouseEvent.

MouseEvent is screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, Button, buttons, EventTarget? RelatedTarget.

Buttons are the types of mouse buttons that are left, right, and wheel. So instead of a number, 0 is the left button, 1 is the scroll wheel, 2 is the right button, and everything else is greater than 2.

As you can see from the above, mouseEvent only knows where we are on the screen and what action (the mouse action) we are doing, not what element we are on. This is what FastClick restores to the end of the user click event.

clickEvent.initMouseEvent(this.determineEventType(targetElement), true.true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false.false.false.false.0.null);
// detremineEvenType is the type of mouseEvent that fastClick encapsulatesCopy the code

Initialize a mouse event, and dispatch the mouse event. The browser automatically responds to subsequent operations.

Now look at the definition of click, as shown below:





Click the properties of the

Click should be a topmost Event target. Mozilla’s definition is a bit different with currentTarget and Type.





Mozilla click

Let’s start with the definition of EventTarget: EventTarget is an interface implemented by objects that can receive events and may have listeners for them.

Element, document, and window are the most common event targets, but other objects can be event targets too, for example XMLHttpRequest, AudioNode,AudioContext, and others.

By definition, if it is a Click event, there must be a target to hold the mouse event. In general, target is either element or Document, and if neither is present then it is a Window object. This should make a little bit of sense, but this is the browser’s event mechanism.





event-flow

So that’s what the browser does after initMouseEvent, to find out if there’s a target to respond to that event, and if there’s no target to respond to, then it’s going to go to the window, and normally we don’t do event handling on the Window, we’re going to get no response, and the event is over. If, by chance, a target (normally element) responds, the binding function is executed.

To summarize the process: The user clicks on the screen, and within 300ms, the browser intercepts the action. Instead of actually triggering the click event attached to the relevant element, the browser records the relevant operation data and waits for the next operation. Since we use the zepto library to bind the tap event, the listener touchend is triggered in the event. Execute relevant operation immediately, hide shell layer. At 300ms, the browser thinks the action is Click instead of dbclick, init a mouseEvent at the same screen location, then start the event mechanism, find an element in the same location that is bound to the click handler, execute this function, Over!! This is how penetration occurs. PS: The browser behavior part is guesswork, not verification.

As for solutions: There are many on the web, the best one is FastClick, but fastClick also has other issues, such as swiping and clicking. The other is to use Zepto but preventDefault.

Android’s own Chrome has been fixed and can be used in other ways, official documentation, and Safari currently supports it, but on higher versions, see the Fastclick issue for a discussion