FastClick 메 우기 및 소스 코드 분석

28752 단어 FastClick소스 코드
최근 제품 여동생 이 체험 문 제 를 제기 했다.iOS 로 손 Q 독서 친구 교류 구역 에서 서평 을 발표 할 때 커서 클릭 은 항상 정확 한 위 치 를 찾 지 못 한다.

위의 그림 과 같이 구체 적 인 표현 은 빠 른 클릭 시 커서 가 textarea 내용 의 끝으로 이동 하 는 것 이다.머 무 르 는 시간 이 좀 오래 걸 릴 때(예 를 들 어 150 ms 초과)커서 를 올 바른 위치 로 정상적으로 찾 을 수 있 습 니 다.
처음에 나 는 iOS 원생 의 상호작용 문제 라 고 생각 했 지만 나중에 일부 페이지 를 방문 하 는 것 은 이런 이상 한 체험 이 없 었 다.
그리고 JS 가 어떤 사건 으로 인 한 문 제 를 등 록 했 는 지 의 심 스 러 워 업무 모듈 을 제거 하고 다시 한 번 뛰 어 보 니 문제 가 예전 과 같 았 다.
그래서 배제 법 을 계속 할 수 밖 에 없 었 습 니 다.페이지 에 있 는 라 이브 러 리 를 조금씩 옮 기 고 페이지 를 실 행 했 습 니 다.그 결과 방해 하 는 임 프 가 과연 혐의 가 가장 큰 Fastclick 인 것 을 발 견 했 습 니 다.
그 다음 에 저 는 API 의 말 에 따라 textarea 에'needsclick'이라는 유형 명 을 붙 여 보 았 습 니 다.fastclick 의 처 리 를 피해 원생 클릭 사건 으로 가 려 고 했 는데 놀 라 서 쓸모 가 없다 는 것 을 알 게 되 었 습 니 다.
이에 감 사 드 립 니 다.저희 팀 의 kindeng 어린이 신발 이 연구 와 해결 방안 을 제 공 했 습 니 다.그런데 저 는 어떤 이유 로 이 구덩이 가 생 겼 는 지 더 연구 하고 싶 습 니 다.Fastclick 은 제 페이지 에 신마 가 되 었 습 니 다.
그래서 어젯밤 에 시간 이 좀 걸 려 서
이것 은 매우 긴 문장 이지 만 주석 이 매우 상세 한 분석 문 일 것 이다.
글 에서 분석 한 소스 코드 를 나 도 내 것 에 걸 었 다github 창고관심 있 는 동 화 는 다운로드 해서 볼 수 있다.
잡담 은 그만 하고 패스 트 클릭 소스 진영 에 깊이 들 어가 자.
우 리 는 FastClick 이 벤트 를 등록 하 는 것 이 매우 간단 하 다 는 것 을 알 고 있 습 니 다.이것 은 다음 과 같 습 니 다.

if ('addEventListener' in document) {
  document.addEventListener('DOMContentLoaded', function() {
    var fc = FastClick.attach(document.body); //    
  }, false);
}
그래서 우 리 는 여기 서부 터 소스 코드 를 열 어 FastClick.attach 방법 을 보 겠 습 니 다.

  FastClick.attach = function(layer, options) {
    return new FastClick(layer, options);
  };
여기 서 FastClick 인 스 턴 스 를 되 돌려 주 었 습 니 다.그래서 우 리 는 앞으로 끌 고 가서 FastClick 구조 함 수 를 봅 시다.

function FastClick(layer, options) {
    var oldOnClick;
    options = options || {};
    //       ...
    //               ,     
    if (FastClick.notNeeded(layer)) {
      return;
    }
    //   ,        Function.prototype.bind     
    //       layer.addEventListener('click', this.onClick.bind(this), true);
    function bind(method, context) {
      return function() { return method.apply(context, arguments); };
    }
    var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
    var context = this;
    for (var i = 0, l = methods.length; i < l; i++) {
      context[methods[i]] = bind(context[methods[i]], context);
    }

    //        
    if (deviceIsAndroid) {
      layer.addEventListener('mouseover', this.onMouse, true);
      layer.addEventListener('mousedown', this.onMouse, true);
      layer.addEventListener('mouseup', this.onMouse, true);
    }
    layer.addEventListener('click', this.onClick, 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);
    //       stopImmediatePropagation     (   Android 2)
    if (!Event.prototype.stopImmediatePropagation) {
      layer.removeEventListener = function(type, callback, capture) {
        var rmv = Node.prototype.removeEventListener;
        if (type === 'click') {
          rmv.call(layer, type, callback.hijacked || callback, capture);
        } else {
          rmv.call(layer, type, callback, capture);
        }
      };
      layer.addEventListener = function(type, callback, capture) {
        var adv = Node.prototype.addEventListener;
        if (type === 'click') {
          //     callback.hijacked      event.propagationStopped        (   onMouse  )     
          //  onMouse       event.propagationStopped    true
          adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
              if (!event.propagationStopped) {
                callback(event);
              }
            }), capture);
        } else {
          adv.call(layer, type, callback, capture);
        }
      };
    }

    //   layer   DOM    onclick   ,           addEventListener     
    if (typeof layer.onclick === 'function') {
      oldOnClick = layer.onclick;
      layer.addEventListener('click', function(event) {
        oldOnClick(event);
      }, false);
      layer.onclick = null;
    }
  }
처음에 FastClick.notNeeded 방법 을 통 해 후속 처리 가 필요 한 지 판단 합 니 다.

    //               ,     
    if (FastClick.notNeeded(layer)) {
      return;
    }
이 FastClick.notNeeded 가 어떤 판단 을 했 는 지 살 펴 보 겠 습 니 다.

  //         Fastclick    
  FastClick.notNeeded = function(layer) {
    var metaViewport;
    var chromeVersion;
    var blackberryVersion;
    var firefoxVersion;

    //         
    if (typeof window.ontouchstart === 'undefined') {
      return true;
    }
    //   Chrome   ,  Chrome   0
    chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
    if (chromeVersion) {
      if (deviceIsAndroid) { //  
        metaViewport = document.querySelector('meta[name=viewport]');
        if (metaViewport) {
          //    ,   user-scalable="no"   meta     chrome        300ms    ,     Fastclick
          if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
            return true;
          }
          //   Chrome 32      ,    width=device-width   meta        FastClick  
          if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
            return true;
          }
        }

        //             Chrome  ,     FastClick  
      } else {
        return true;
      }
    }

    if (deviceIsBlackBerry10) { //  ,       ,      
      blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
      if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
        metaViewport = document.querySelector('meta[name=viewport]');
        if (metaViewport) {
          if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
            return true;
          }
          if (document.documentElement.scrollWidth <= window.outerWidth) {
            return true;
          }
        }
      }
    }
    //    -ms-touch-action: none / manipulation     IE10        ,    300ms   
    if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
      return true;
    }

    // Firefox  ,  
    firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
    if (firefoxVersion >= 27) {
      metaViewport = document.querySelector('meta[name=viewport]');
      if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
        return true;
      }
    }
 
    // IE11       “-ms-”    touch-action      
    if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
      return true;
    }
    return false;
  };
기본적으로 300 ms 지연 을 사용 하지 않 는 브 라 우 저 탐색 입 니 다.Fastclick 을 사용 할 필요 가 없 기 때문에 true 리 턴 구조 함수 로 돌아 가 다음 실행 을 중단 합 니 다.
안 드 로 이 드 손 Q 의 ua 가/chrome\/([0-9]+)/와 일치 하기 때문에'user-scalable=no'meta 태그 가 있 는 안 드 로 이 드 손 Q 페이지 는 FastClick 에 의 해 처리 할 필요 가 없 는 페이지 로 간 주 됩 니 다.
안 드 로 이 드 의 손 Q 에서 문 제 를 처음 언급 하지 않 은 이유 다.
우 리 는 구조 함 수 를 계속 보 았 습 니 다.레이 어(즉 body)에 click,touch start,touch move,touch end,touch cancel(안 드 로 이 드 와 mouseover,mousedown,mouseup)사건 감청 을 직접 추 가 했 습 니 다.

    //        
    if (deviceIsAndroid) {
      layer.addEventListener('mouseover', this.onMouse, true);
      layer.addEventListener('mousedown', this.onMouse, true);
      layer.addEventListener('mouseup', this.onMouse, true);
    }

    layer.addEventListener('click', this.onClick, 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);
이 코드 에 bid 방법 을 사용 하여 처리 하 였 음 을 주의 하 십시오.이 이벤트 리 셋 의 this 는 모두 Fastclick 인 스 턴 스 컨 텍스트 가 됩 니 다.
또 onclick 사건 과 안 드 로 이 드 의 추가 처리 부분 은 모두 캡 처 감청 이 었 음 을 유의 해 야 한다.
우 리 는 이 사건 들 이 각각 무엇 을 했 는 지 보 자.
1. this.onTouchStart

  FastClick.prototype.onTouchStart = function(event) {
    var targetElement, touch, selection;
    //           
    if (event.targetTouches.length > 1) {
      return true;
    }
    targetElement = this.getTargetElementFromEventTarget(event.target); //        ,target           ,    DOM  
    touch = event.targetTouches[0];
    if (deviceIsIOS) { //IOS  
      //             (             ),   
      selection = window.getSelection();
      if (selection.rangeCount && !selection.isCollapsed) {
        return true;
      }
      if (!deviceIsIOS4) { //  IOS4
        //      ―― click         alert/confirm,     tap        ,  touchstart touchend
        //        touch.identifier(   touch event        alert    touch event   ),
        //      event     event    ,        
        //  chrome       'Emulate touch events' ,iOS UA   identifier    0,                   
        if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
          event.preventDefault();
          return false;
        }
        this.lastTouchIdentifier = touch.identifier;
        //   target              (    -webkit-overflow-scrolling: touch) ,    :
        // 1)                
        // 2)     tap          
        //       'touchend' event.target               
        //             ,     target          ,   ,    
        //  touchend         (     scrolltop)     ,            ,    fastclick  
        this.updateScrollParent(targetElement);
      }
    }
    this.trackingClick = true; //          click   
    this.trackingClickStart = event.timeStamp; //   touch        
    this.targetElement = targetElement;
    //  touch         
    this.touchStartX = touch.pageX;
    this.touchStartY = touch.pageY;
    // this.lastClickTime    touchend          
    // this.tapDelay     200 (ms)
    //        phantom    (200ms       )   click
    //   200ms                 
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
      event.preventDefault();
    }
    return true;
  };
가 는 길에 여기 this.updateScroll Parent 를 보 세 요.


  /**
   *   target             ,          
   */
  FastClick.prototype.updateScrollParent = function(targetElement) {
    var scrollParent, parentElement;
    scrollParent = targetElement.fastClickScrollParent;
    if (!scrollParent || !scrollParent.contains(targetElement)) {
      parentElement = targetElement;
      do {
        if (parentElement.scrollHeight > parentElement.offsetHeight) {
          scrollParent = parentElement;
          targetElement.fastClickScrollParent = parentElement;
          break;
        }
        parentElement = parentElement.parentElement;
      } while (parentElement);
    }
    //          fastClickLastScrollTop,           
    if (scrollParent) {
      scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
    }
  };
또한,onTouchStart 에 true 로 표 시 된 this.tracking Click 속성 은 다른 이벤트 리 셋(예 를 들 어 ontouchmove)의 시작 부분 에서 검 사 를 합 니 다.할당 되 지 않 았 다 면 무시 합 니 다.

    if (!this.trackingClick) {
      return true;
    }
물론 ontouchend 사건 에 서 는 false 로 초기 화 합 니 다.
2. this.onTouchMove
이 코드 의 양은 매우 적다.

  FastClick.prototype.onTouchMove = function(event) {
    //       click      
    if (!this.trackingClick) {
      return true;
    }
    //   target     ,                click
    //      this.trackingClick this.targetElement,               
    if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
      this.trackingClick = false;
      this.targetElement = null;
    }
    return true;
  };
여기에 사 용 된 this.touch HasMoved 원형 방법 을 보 세 요:

  //       
  //this.touchBoundary   ,  10
  //  touch     10      ,             click  
  FastClick.prototype.touchHasMoved = function(event) {
    var touch = event.changedTouches[0], boundary = this.touchBoundary;
    if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
      return true;
    }
    return false;
  };
3. onTouchEnd


  FastClick.prototype.onTouchEnd = function(event) {
    var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
    if (!this.trackingClick) {
      return true;
    }
    //    phantom    (200ms       )   click
    //     ontouchstart           (        ),        
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
      this.cancelNextClick = true; //      onMouse       , true          
      return true;
    }
    //this.tapTimeout   ,  700
    //         ,   (  700ms)   
    if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
      return true;
    }
    //     false,  input       
    //     https://github.com/ftlabs/fastclick/issues/156
    this.cancelNextClick = false;
    this.lastClickTime = event.timeStamp; //  touchend  ,      touchstart     
    trackingClickStart = this.trackingClickStart;
    //   this.trackingClick   this.trackingClickStart
    this.trackingClick = false;
    this.trackingClickStart = 0;
    // iOS 6.0-7.*        ――   layer  transition scroll  ,event    target     
    //         targetElement(     document.elementFromPoint      )
    if (deviceIsIOSWithBadTarget) { //iOS 6.0-7.*  
      touch = event.changedTouches[0]; //        
      //       elementFromPoint         /    ,        targetElement   null
      targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
      // target         , fastClickScrollParent     
      targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
    }
    targetTagName = targetElement.tagName.toLowerCase();
    if (targetTagName === 'label') { // label         
      forElement = this.findControl(targetElement);
      if (forElement) {
        this.focus(targetElement);
        //      (    click    ,           ,     )
        if (deviceIsAndroid) {
          return false;
        }
        targetElement = forElement;
      }
    } else if (this.needsFocus(targetElement)) { // label       focus   
      //             100ms,   this.targetElement   
      //(       this.focus        ,     click/focus      )
      //               ,      (  100ms)textarea                 
      //  iOS        bug――              iframe  ,     focus  ,
      //          text     (  value    ),so       
      if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
        this.targetElement = null;
        return false;
      }
      this.focus(targetElement);
      this.sendClick(targetElement, event); //     click  ,     300ms
      //iOS4   select           (        ),      select  
      //    iOS6/7  (VoiceOver      )    
      if (!deviceIsIOS || targetTagName !== 'select') {
        this.targetElement = null;
        event.preventDefault();
      }

      return false;
    }
    if (deviceIsIOS && !deviceIsIOS4) {
      //               ,             ,   
      scrollParent = targetElement.fastClickScrollParent;
      if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
        return true;
      }
    }
    //                (      “needsclick” class)
    //                ,       click  
    if (!this.needsClick(targetElement)) {
      event.preventDefault();
      this.sendClick(targetElement, event);
    }
    return false;
  };
이 단락 은 비교적 길다.우 리 는 주로 이 단락 을 본다.


    } else if (this.needsFocus(targetElement)) { // label       focus   
      //             100ms,   this.targetElement   
      //(       this.focus        ,     click/focus      )
      //               ,      (  100ms)textarea                 
      //  iOS        bug――              iframe  ,     focus  ,
      //          text     (  value    ),so       
      if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
        this.targetElement = null;
        return false;
      }
      this.focus(targetElement);
      this.sendClick(targetElement, event); //     click  ,     300ms

      //iOS4   select           (        ),      select  
      //    iOS6/7  (VoiceOver      )    
      if (!deviceIsIOS || targetTagName !== 'select') {
        this.targetElement = null;
        event.preventDefault();
      }
      return false;
    }
그 중에서 this.needs Focus 는 주어진 요소 가 click 이 벤트 를 합성 하여 초점 을 모 의 해 야 하 는 지 판단 하 는 데 사 용 됩 니 다.

  //              click       
  FastClick.prototype.needsFocus = function(target) {
    switch (target.nodeName.toLowerCase()) {
      case 'textarea':
        return true;
      case 'select':
        return !deviceIsAndroid; //iOS  select        
      case 'input':
        switch (target.type) {
          case 'button':
          case 'checkbox':
          case 'file':
          case 'image':
          case 'radio':
          case 'submit':
            return false;
        }
        return !target.disabled && !target.readOnly;
      default:
        //    “bneedsfocus” class   true
        return (/\bneedsfocus\b/).test(target.className);
    }
  };
또한 이 부분 은 왜 조금 만 오래 누 르 는 지 설명 합 니 다(100 ms 초과)textarea.우 리 는 커서 를 정확 한 곳 에 위치 시 킬 수 있 습 니 다(뒤 를 돌아 this.focus 를 호출 하 는 방법).

      //             100ms,   this.targetElement   
      //(       this.focus        ,     click/focus      )
      //               ,      (  100ms)textarea                 
      //  iOS        bug――              iframe  ,     focus  ,
      //          text     (  value    ),so       
      if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
        this.targetElement = null;
        return false;
      }
이어서 우 리 는 이 두 줄 의 매우 중요 한 코드 를 보 자.

this.focus(targetElement);
this.sendClick(targetElement, event); //     click  ,     300ms
관련 된 두 가지 원형 방법 은 다음 과 같다.
⑴ this.focus

  FastClick.prototype.focus = function(targetElement) {
    var length;

    //       setSelectionRange(selectionStart, selectionEnd)       (         
    //         sendClick       )
    //    iOS7    input  (   date datetime month)   selectionStart   selectionEnd          ,
    //           setSelectionRange      ,       focus     
    if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
      length = targetElement.value.length;
      targetElement.setSelectionRange(length, length);
    } else {
      //     focus  
      targetElement.focus();
    }
  };
주의,우 리 는 textarea 를 클릭 할 때 이 방법 을 호출 했 습 니 다.targetElement.set Selection Range(length,length)를 통 해 커서 의 위 치 를 내용 의 끝 에 결정 하 였 습 니 다(그러나 주의,이 때 는 초점 을 맞 추 지 않 았 습 니 다!!).
⑵ this.sendClick
진정 으로 textarea 를 초점 을 맞 추 는 것 은 이 방법 입 니 다.click 방법 을 합성 하여 즉시 textarea 요소 에 초점 을 맞 추 는 것 입 니 다.

  //    click           
  FastClick.prototype.sendClick = function(targetElement, event) {
    var clickEvent, touch;

    //         ,         activeElement(     ,  input)  ,     click     
    if (document.activeElement && document.activeElement !== targetElement) {
      document.activeElement.blur();
    }

    touch = event.changedTouches[0];

    //   (Synthesise)    click   
    //                (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; // fastclick     ,    click          
    targetElement.dispatchEvent(clickEvent); //     click  
  };

  FastClick.prototype.determineEventType = function(targetElement) {

    //      Select         click      ,    mousedown
    if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
      return 'mousedown';
    }

    return 'click';
  };
이렇게 고생 한 후에 우 리 는 textarea 를 가볍게 한 후에 커서 는 자 연 스 럽 게 그 내용 의 끝 부분 에 위치 하 게 되 었 다.그런데 여기 문제 가 있 습 니 다.터치 엔 드 뒤에 있 는 포커 스 사건 은 왜 촉발 되 지 않 았 습 니까?
focus 이벤트 가 촉발 된다 면 커서 를 정확 한 위치 로 다시 찾 을 수 있 을 것 입 니 다.
우리 아래 의 이 단락 을 보 자.

      //iOS4   select           (        ),      select  
      //    iOS6/7  (VoiceOver      )    
      if (!deviceIsIOS || targetTagName !== 'select' ) {
        this.targetElement = null;
        event.preventDefault();
      }
preventDefault 의 차단 을 통 해 textarea 는 더 이상 focus 아 기 를 안 을 수 없습니다~
그래서 우 리 는 여기 서 변경 을 하면 이 문 제 를 복구 할 수 있 습 니 다.

      var _isTextInput = function(){
        return targetTagName === 'textarea' || (targetTagName === 'input' && targetElement.type === 'text');
      };
      
      if ((!deviceIsIOS || targetTagName !== 'select') && !_isTextInput()) {
        this.targetElement = null;
        event.preventDefault();
      }
또는:

if (!deviceIsIOS4 || targetTagName !== 'select') {
  this.targetElement = null;
  // textarea  “needsclick” class
  if((!/\bneedsclick\b/).test(targetElement.className)){
    event.preventDefault(); 
  }
}
여기 서 토로 할 것 은 Fastclick 이 this.needs Click 을 ontouchEnd 끝 에 놓 고 실행 해서 앞에서 말 한 것 에'needs click'이라는 이름 을 붙 여도 무효 라 는 문제 가 생 겼 다 는 것 이다.
비록 문제 의 원인 을 찾 아 도 해결 되 었 지만,우 리 는 남 은 부분 을 계속 보 자.
4.onMouse 와 onClick

  //            (  layer click    )
  FastClick.prototype.onMouse = function(event) {
    // touch       
    if (!this.targetElement) {
      return true;
    }
    if (event.forwardedTouchEvent) { //   click      
      return true;
    }
    //                    
    //         preventDefault   (event.cancelable    true)  
    if (!event.cancelable) {
      return true;
    }
    //             ,      (200ms)     
    if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
      //           
      if (event.stopImmediatePropagation) {
        event.stopImmediatePropagation();
      } else {
        //     stopImmediatePropagation    (  Android 2)   ,
        //            ( 126 )
        event.propagationStopped = true;
      }
      //        
      event.stopPropagation();
      event.preventDefault();
      return false;
    }
    //    
    return true;
  };
  //click      touch      ,   touch    。
  //       touch              event  ,      click        
  //              (   )
  FastClick.prototype.onClick = function(event) {
    var permitted;
    //      trackingClick   ,     UI     touchEnd    
    if (this.trackingClick) {
      this.targetElement = null;
      this.trackingClick = false;
      return true;
    }
    //      iOS         ――        iOS            submit  
    //              “Go”  ,     “ ”click  (target   submit-type input  )
    if (event.target.type === 'submit' && event.detail === 0) {
      return true;
    }
    permitted = this.onMouse(event);
    if (!permitted) { //         , this.targetElement      onMouse           
      this.targetElement = null;
    }
    //      
    return permitted;
  };

  //  Fastclick        。          
  FastClick.prototype.destroy = function() {
    var layer = this.layer;
    if (deviceIsAndroid) {
      layer.removeEventListener('mouseover', this.onMouse, true);
      layer.removeEventListener('mousedown', this.onMouse, true);
      layer.removeEventListener('mouseup', this.onMouse, true);
    }
    layer.removeEventListener('click', this.onClick, true);
    layer.removeEventListener('touchstart', this.onTouchStart, false);
    layer.removeEventListener('touchmove', this.onTouchMove, false);
    layer.removeEventListener('touchend', this.onTouchEnd, false);
    layer.removeEventListener('touchcancel', this.onTouchCancel, false);
  };
일반적인 클릭 이벤트 의 조작 을 차단 해 야 합 니 다.저 희 는 touch 감청 이벤트 리 셋 에서 처 리 했 습 니 다.여 기 는 주로 touch 과정(일부 장치 가 touch 이벤트 가 촉발 되 지 않 았 을 수도 있 습 니 다)에 대해 기본 이벤트 의 이 벤트 를 사용 하지 않 고 더 이상 처리 하지 않 았 습 니 다.원생 의 click 이 벤트 를 촉발 할 지 여 부 를 결정 합 니 다.
작은 매듭
1.fastclick 소스 코드 의 addEventListener 리 셋 이벤트 에는 return false/true 가 많 습 니 다.그것들 은 뒤에 있 는 스 크 립 트 논 리 를 돌아 가 는 데 주로 사용 되 며,다른 의미 가 없습니다.(기본 이 벤트 를 막 지 않 습 니 다.)
그 러 니 jQuery 사건 이나 DOM 0 급 사건 의 리 턴 false 개념 을 addEventListener 와 섞 지 마 세 요.
2.fastclick 의 소스 코드 는 매우 간단 합 니 다.일부 기괴 한 행위 에 대해 hack 를 하 는 것 외 에 그 핵심 이념 은 target 사건 을 캡 처 하고 target 이 투과 문 제 를 해결 하려 는 요소 라 고 판단 하 며 하나의 click 사건 을 합성 하여 target 에서 촉발 하 는 동시에 preventDefault 를 통 해 기본 사건 을 사용 하지 않 습 니 다.
3.fastclick 는 좋 지만 구덩이 도 있 습 니 다.수요 에 따라 수정 해 야 합 니 다.그러면 소스 코드 를 알 아 보 는 것 이 필요 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기