End mobile touch, click on the event optimization (fastclick source learning)

End mobile touch, click on the event optimization (fastclick source learning)

Recently doing some of the micro-channel mobile end page, on this record the learning process optimization on mobile end touch-and-click event, the main content revolves around fastclick.
fastclick github

The origin of

Mobile browser generally end after the user clicks on the screen will be delayed approximately 300ms before the Event trigger the Click
- GOOGLE

Phone Open this link to view the delay Demo
(Many browsers have delayed the problem does not exist, see fastclick github, but the author's mobile browser is still there three hundred millisecond delay problem)
shots are as follows
image description

Why 300ms delay it, there is a major double-click the zoom function, the browser needs to determine whether the user clicks to double click to zoom. This problem is not solved,
1, the user experience will be poor, very smooth, especially in intensive operations at the scene, such as a calculator, 300ms delay does not solve the problem, I feel very slow reaction;
2, click penetrating questions

Event trigger sequence

Before looking at the idea fastclick, we look at the sequence of events triggered what

  • touchstart
  • touchmove
  • touchEnd
  • mouseover: when the element moves a pointing device or the presence of listeners of the sub-elements, mouseover event is triggered.
  • mouseenter: when the pointing device (usually a mouse) is moved in the element, mousemove event is triggered.
  • mousedown
  • click

Mobile end click 300ms latency issues there, touch can not, oh.

fastclick ideas

fastclick idea is to simulate the use of touch tap (touch), if considered to be a valid tap, then simulate a click event immediately upon touchend, distributed to the event source (the equivalent of active trigger a click), while blocking out the browser click arising after 300ms.

Source learning

Look at the example of the use of very simple, we have been thinking along attach to go.

if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
    }, false);
}

Fastlick bind directly to the body on the line - -.
See the source code structure (Note: All of the following codes are removed that do not affect the understanding of the idea of part, most of the ideas written in comments)

//构造函数
    function FastClick(layer, options)
//判断是否需要浏览器原生的click事件(针对一些特殊元素比如表单)
    FastClick.prototype.needsClick = function(target)
//发送模拟的click event
    FastClick.prototype.sendClick = function(targetElement, event)
// touchstart eventhandler
    FastClick.prototype.onTouchStart = function(event)
// touchmove eventhandler
    FastClick.prototype.onTouchMove = function(event)
// touchend eventhandler
    FastClick.prototype.onTouchEnd = function(event)
// 判断这次tap是否有效
    FastClick.prototype.onMouse = function(event) 
//click handler 捕获阶段监听
    FastClick.prototype.onClick = function(event)
//销毁fastlick,移除事件绑定
    FastClick.prototype.destroy = function()
//绑定接口
    FastClick.attach = function(layer, options) {
        return new FastClick(layer, options);
    };

attach the actual implementation of the constructor to initialize, then we look at what happened constructor

    function FastClick(layer,options){
        //一些属性初始化
        //安卓一些老版本浏览器不支持bind, poly fill
        function bind (method, context) {
          return function () {
            return method.apply(context, arguments);
          };
        }
        var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 
        'onTouchEnd', 'onTouchCancel'];
        var context = this;
        //将所有handler的this绑定到fastclick实例
        for (var i = 0, l = methods.length; i < l; i++) {
            context[methods[i]] = bind(context[methods[i]], context);
        }
        //为当前fast click对象绑定的layer(我们的示例中时document.body)加监听
        layer.addEventListener('click', this.onClick, true);//true 捕获阶段触发 
        layer.addEventListener('touchstart', this.onTouchStart, false);
        layer.addEventListener('touchmove', this.onTouchMove, false);
        layer.addEventListener('touchend', this.onTouchEnd, false);
        layer.addEventListener('touchcancel', this.onTouchCancel, false);
    }

The main constructor is to initialize some properties, polyfill, and add a listener,
let's start to look at how the main event, touchstart, touchend is to determine whether or not effectively tap, how to simulate a click event, click the how to stop 300ms
touchstart

  FastClick.prototype.onTouchStart = function (event) {
    var targetElement, touch, selection;

    // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
    // 如果多触点可能是在缩放,不对targetElement初始化,在此提前终止避免误模拟产生click
    if (event.targetTouches.length > 1) {
      return true;
    }

    //获取发生事件源元素(目标阶段的元素)
    targetElement = this.getTargetElementFromEventTarget(event.target);
    touch = event.targetTouches[0];
    
    this.trackingClick = true;//标记开始跟踪click
    this.trackingClickStart = event.timeStamp;//开始跟踪时间
    this.targetElement = targetElement;//事件源元素

    //触摸坐标,接下来判断是否越界用到
    this.touchStartX = touch.pageX;
    this.touchStartY = touch.pageY;

    // Prevent phantom clicks on fast double-tap (issue #36)
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
      event.preventDefault();//阻止之后的click
    }

    return true;
  };

touchstart mainly some tap initialization-related properties tracked for subsequent determination '
Next touchmove

 FastClick.prototype.onTouchMove = function (event) {
    if (!this.trackingClick) {
      return true;
    }

    // If the touch has moved, cancel the click tracking 移动到了其他元素
    if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {//移动越界了,取消本次click模拟处理,走原生流程
      this.trackingClick = false;
      this.targetElement = null;
    }

    return true;
  };

touchmove relatively simple, largely compatible with a sliding tap (swiper) and the like, not sliding out of range click simulation
The following is touchend

FastClick.prototype.onTouchEnd = function (event) {
    var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;

    if (!this.trackingClick) {
      return true;
    }

    // Prevent phantom clicks on fast double-tap (issue #36)
    //阻止快速双击
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
      this.cancelNextClick = true;
      return true;
    }
    //超时就不算click了,走原生流程,不阻止click
    if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
      return true;
    }

    this.lastClickTime = event.timeStamp;

    this.trackingClick = false;
    this.trackingClickStart = 0;



    // Prevent the actual click from going though - unless the target node is marked as requiring
    // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
    if (!this.needsClick(targetElement)) {
      event.preventDefault();//阻止之后的click
      this.sendClick(targetElement, event);//发送模拟click
    }

    return false;
  };
 //发送模拟的click event
  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];

    //模拟click
    // 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分发模拟的click
    targetElement.dispatchEvent(clickEvent);
  };

Finally, also captures the stage of listening click layer

  //click handler 捕获阶段监听
  FastClick.prototype.onClick = function (event) {
    var permitted;
    // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
    if (this.trackingClick) {//1、出界会置为false,2成功模拟了一次完成tap并阻止click也会置为false,3、避免三方库影响
      this.targetElement = null;
      this.trackingClick = false;
      return true;
    }

    // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
    if (event.target.type === 'submit' && event.detail === 0) {
      return true;
    }

    permitted = this.onMouse(event);

    // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
    if (!permitted) {
      this.targetElement = null;
    }

    // If clicks are permitted, return true for the action to go through.
    return permitted;
  };

 // 判断这次鼠标是否有效
  FastClick.prototype.onMouse = function (event) {

    // If a target element was never set (because a touch event was never fired) allow the event
    if (!this.targetElement) {
      return true;
    }

    // 标记fastclick模拟产生的event
    if (event.forwardedTouchEvent) {
      return true;
    }

    // Programmatically generated events targeting a specific element should be permitted
    if (!event.cancelable) {
      return true;
    }

    // Derive and check the target element to see whether the mouse event needs to be permitted;
    // unless explicitly enabled, prevent non-touch click events from triggering actions,
    // to prevent ghost/doubleclicks.
    // 是否需要原生的click
    if (!this.needsClick(this.targetElement) || this.cancelNextClick) {

      // Prevent any user-added listeners declared on FastClick element from being fired.
      if (event.stopImmediatePropagation) {
        event.stopImmediatePropagation();
      } else {

        // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
        event.propagationStopped = true;
      }

      // Cancel the event 阻止事件捕获和冒泡
      event.stopPropagation();
      event.preventDefault();

      return false;
    }

    // If the mouse event is permitted, return true for the action to go through.
    return true;
  };

This is mainly to determine the click is valid (such as invalid, then stop the capture and bubbling)
At this point the basic process has ended.
Which has a point to note, the author of chrome (Version 64.0.3282.119 (Official Build) (64-bit)) have been tested
stopPropagation, stopImmediatePropagation will not only stop the capture process also prevents bubbling oh.

At last

Recommended reading source code, source code, there are many treatment on focus, different browsers compatible and special form element fastclick github.
Here is the code author with Chinese comments Chinese commented code.
If flawed, welcome criticism.

Reference

MDN
https://juejin.im/entry/55d73...

Guess you like

Origin www.cnblogs.com/jlfw/p/11914823.html