웹 프론트엔드 JS 튜토리얼 fastclick 코드에서 탭 '클릭 연결'을 해결하는 방법

fastclick 코드에서 탭 '클릭 연결'을 해결하는 방법

Jun 15, 2018 pm 05:39 PM

이 글에서는 FastClick 소스 코드 분석을 통해 탭 "클릭 연결" 문제를 완전히 해결하기 위한 지식 콘텐츠를 주로 소개합니다.

최근 탭 이벤트 사용으로 인해 여러 가지 문제가 발생했습니다. 문제 중 하나는 문제를 해결하려면 원래 클릭을 탭으로 변경해야 한다는 것입니다. 이 경우 IE 사용자는

물론 호환성을 포기합니다. 하지만 아무도 이전 코드를 건드리고 싶어하지 않았기 때문에 오늘은 fastclick 문제를 생각해냈습니다.

최근에 탭스루 사건에 대해 포스팅한 것은 이번이 네 번째입니다. "클릭스루" 마스크, 그래서 오늘 상사가 fastclick이라는 라이브러리를 제안했는데, 그것이 마침내 우리의 문제를 해결했음을 입증했습니다

클릭을 탭으로 대체할 필요가 없기 때문에 우리 상사는 저에게 진지하게 말했습니다. 오해합니다. 이메일을 모두 보냈습니다. .....

그래서 문제가 해결될 수 있는지 확인하기 위해 오후에 fastclick 라이브러리를 살펴보았습니다. 그럼 시작해 보겠습니다.

fastclick 소스 코드 읽기

Nima, 사용하기 너무 간단합니다.

FastClick.attach(document.body);
로그인 후 복사

라고 말하세요. 이제 모든 클릭 응답 속도가 직접적으로 향상됩니다! 어떤 입력이 포커스를 받는 문제도 해결! ! ! 젠장, 정말 가능하다면 페이지를 바꾼 동료가 분명 나를 힘들게 할 거야

차근히 따라가자, 입구는 첨부 방식이다:

FastClick.attach = function(layer) {
'use strict';
return new FastClick(layer);
};
로그인 후 복사

이 형이 방금 코드를 인스턴스화했으니까 아직 살펴봐야 합니다. 생성자:

function FastClick(layer) {
'use strict';
var oldOnClick, self = this;
  this.trackingClick = false;
  this.trackingClickStart = 0;
  this.targetElement = null;
  this.touchStartX = 0;
  this.touchStartY = 0;
  this.lastTouchIdentifier = 0;
  this.touchBoundary = 10;
  this.layer = layer;
  if (!layer || !layer.nodeType) {
   throw new TypeError('Layer must be a document node');
  }
  this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };
  this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };
  this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };
  this.onTouchMove = function() { return FastClick.prototype.onTouchMove.apply(self, arguments); };
  this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };
  this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };
  if (FastClick.notNeeded(layer)) {
   return;
  }
  if (this.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);
 
  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') {
     adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
      if (!event.propagationStopped) {
       callback(event);
      }
     }), capture);
    } else {
     adv.call(layer, type, callback, capture);
    }
   };
  }
  if (typeof layer.onclick === 'function') {
   oldOnClick = layer.onclick;
   layer.addEventListener('click', function(event) {
 oldOnClick(event);
 }, false);
 layer.onclick = null;
}
}
로그인 후 복사

이 코드를 보세요. 위의 속성 중 많은 부분이 어떤 역할을 하는지 모르겠습니다... 그래서

if (!layer || !layer.nodeType) {
throw new TypeError('Layer must be a document node');
}
로그인 후 복사

를 무시했습니다. 그렇지 않으면 문제가 발생합니다

그런 다음 이 사람은 자신의 속성 메서드에 몇 가지 기본 마우스 이벤트를 등록합니다. 자세한 내용은 나중에 이야기하겠습니다

뒤에 notNeeded 메서드가 있습니다.

 FastClick.notNeeded = function(layer) {
  'use strict';
  var metaViewport;
  if (typeof window.ontouchstart === 'undefined') {
   return true;
  }
  if ((/Chrome\/[0-9]+/).test(navigator.userAgent)) {
   if (FastClick.prototype.deviceIsAndroid) {
    metaViewport = document.querySelector('meta[name=viewport]');
    if (metaViewport && metaViewport.content.indexOf('user-scalable=no') !== -1) {
     return true;
    }
   } else {
    return true;
   }
  }
  if (layer.style.msTouchAction === 'none') {
   return true;
  }
  return false;
 };
로그인 후 복사

이 메서드가 사용되었습니다. fastclick을 사용해야 하는지 판단하려면 주석의 의미가 명확하지 않습니다. 코드를 살펴보겠습니다. 첫 번째 문장:

if (typeof window.ontouchstart === 'undefined') {
 return true;
}
로그인 후 복사

터치스타트 이벤트가 지원되지 않으면 true를 반환하세요

PS: 현재 느낌은 다음과 같습니다. 그 fastclick도 동일해야 합니다. 터치 이벤트는 시뮬레이션되었지만 포인트 스루 문제는 없습니다


또한 Android의 몇 가지 문제는 나중에 판단하여 여기서만 지원해야 한다는 의미입니다. 터치를 지원하므로 메인 코드로 돌아갑니다

메인 코드에서 브라우저가 터치 이벤트나 다른 문제를 지원하지 않으면 바로 팝업이 뜹니다

그런 다음 deviceIsAndroid 속성이 있으니 살펴보겠습니다. (사실 안봐도 안드로이드 기기인지 알 수 있어요)

FastClick.prototype .deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0;

Binding events

알겠습니다, 이 사람 등록 이벤트 바인딩을 시작했는데 지금까지 이상한 점은 발견되지 않았습니다

 if (this.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

속성이 하나 더 있습니다.

현재 이벤트의 버블링 동작을 중지하고 현재 이벤트가 있는 요소에서 동일한 유형의 모든 이벤트에 대한 이벤트 핸들러의 지속적인 실행을 방지합니다.

요소에 동일한 유형의 이벤트에 대한 여러 이벤트 리스너 함수가 있는 경우 이 유형의 이벤트가 트리거되면 여러 이벤트 리스너 함수가 순서대로 실행됩니다. 리스닝 함수가 event.stopImmediatePropagation() 메서드를 실행하는 경우 차단되는 이벤트의 버블링 동작(event.stopPropagation 메소드의 역할) 외에도 해당 요소에 바인딩된 동일한 유형의 다른 이벤트도 차단됩니다.

 <html>
     <head>
         <style>
             p { height: 30px; width: 150px; background-color: #ccf; }
             p {height: 30px; width: 150px; background-color: #cfc; }
         </style>
     </head>
     <body>
         <p>
             <p>paragraph</p>
         </p>
         <script>
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第一个监听函数");
             }, false);
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第二个监听函数");
                 event.stopImmediatePropagation();
                 //执行stopImmediatePropagation方法,阻止click事件冒泡,并且阻止p元素上绑定的其他click事件的事件监听函数的执行.
             }, false);
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第三个监听函数");
                 //该监听函数排在上个函数后面,该函数不会被执行.
             }, false);
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素,我是p元素的上层元素");
                 //p元素的click事件没有向上冒泡,该函数不会被执行.
             }, false);
         </script>
     </body>
 </html>
로그인 후 복사
 if (!Event.prototype.stopImmediatePropagation) {
  layer.removeEventListener = function(type, callback, capture) {
   var rmv = Node.prototype.removeEventListener;
   if (type === &#39;click&#39;) {
    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 === &#39;click&#39;) {
    adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
     if (!event.propagationStopped) {
      callback(event);
     }
    }), capture);
   } else {
    adv.call(layer, type, callback, capture);
   }
  };
 }
로그인 후 복사

그러면 이 사람이 재정의됩니다.

먼저 Node의 addEventListener를 사용하는 등록 이벤트를 살펴보겠습니다.

이 관점에서 Node는 우리 노드를 대표하는 시스템 속성이므로 여기서 로그아웃 이벤트를 다시 작성합니다

여기서 실제로는 클릭 시에만 특수 처리를 수행한다는 것을 알 수 있습니다

adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
if (!event.propagationStopped) {
 callback(event);
}
}), capture);
로그인 후 복사

그 중 하나가 하이재킹되었습니다. 하이재킹의 목적이 무엇인지는 모르겠지만 중간에 다시 작성한다는 의미인 것 같습니다. 그러면 여기서 다시 작성했습니다. Hijacked는 아마도 여러 이벤트가 DOM에 등록되어 여러 번 실행되는 것을 방지하기 위한 방법일 것입니다. 의 상황

우리는 로그아웃과 등록에 신경쓰지 않습니다. 실제로 우리가 DOM에 전달한 등록과 로그아웃 이벤트는 향후에 DOM이 사용할 것임을 의미합니다. 클릭 이벤트를 호출하는 클릭 이벤트는 물론 저희의 일시적인 판단이므로 자세한 내용은 읽어봐야 하고 현재 판단은 신뢰할 수 없는 것 같으니 계속 진행하겠습니다

이벤트가 취소되면 저희는 addEventListener 또는 dom.onclick=function(){ }을 사용할 수 있으므로 다음 코드는 다음과 같습니다.

if (typeof layer.onclick === &#39;function&#39;) {
 oldOnClick = layer.onclick;
 layer.addEventListener(&#39;click&#39;, function(event) {
 oldOnClick(event);
 }, false);
 layer.onclick = null;
}
로그인 후 복사

여기서 그의 기본 프로세스는 실제로 완료되었습니다. 이는 입구 또는 출구에 관계없이 그의 모든 논리가 여기에 있음을 의미합니다. 이벤트가 등록되어야 하므로 코드를 작성합니다.

테스트 입구

 <input type="button" value="addevent">
 <input type="button" value="addevent1">
 $(&#39;#addEvent&#39;).click(function () {
     var dom = $(&#39;#addEvent1&#39;)[0]
     dom.addEventListener(&#39;click&#39;, function () {
         alert(&#39;&#39;)
         var s = &#39;&#39;;
     })
 });
로그인 후 복사

를 살펴보겠습니다. 이 중단점을 통해 클릭한 후 무엇을 했는지 살펴보겠습니다. 이제 버튼 1을 클릭하면 버튼 2에 이벤트가 등록됩니다. :

但是很遗憾,我们在电脑上不能测试,所以增加了我们读代码的困难,在手机上测试后,发现按钮2响应很快,但是这里有点看不出问题

最后alert了一个!Event.prototype.stopImmediatePropagation发现手机和电脑都是false,所以我们上面搞的东西暂时无用

 FastClick.prototype.onClick = function (event) {
     &#39;use strict&#39;;
     var permitted;
     alert(&#39;终于尼玛进来了&#39;);
     if (this.trackingClick) {
         this.targetElement = null;
         this.trackingClick = false;
         return true;
     }
     if (event.target.type === &#39;submit&#39; && event.detail === 0) {
         return true;
     }
     permitted = this.onMouse(event);
     if (!permitted) {
         this.targetElement = null;
     }
     return permitted;
 };
로그인 후 복사

然后我们终于进来了,现在我们需要知道什么是trackingClick 了

/**
* Whether a click is currently being tracked.
* @type Boolean
*/
this.trackingClick = false;
로그인 후 복사

我们最初这个属性是false,但是到这里就设置为true了,就直接退出了,说明绑定事件终止,算了这个我们暂时不关注,我们干点其它的,

因为,我觉得重点还是应该在touch事件上

PS:到这里,我们发现这个库应该不只是将click加快,而是所有的响应都加快了

我在各个事件部分log出来东西,发现有click的地方都只执行了touchstart与touchend,于是至此,我觉得我的观点成立
他使用touch事件模拟量click,于是我们就只跟进这一块就好:

 FastClick.prototype.onTouchStart = function (event) {
     &#39;use strict&#39;;
     var targetElement, touch, selection;
     log(&#39;touchstart&#39;);
     if (event.targetTouches.length > 1) {
         return true;
     }
     targetElement = this.getTargetElementFromEventTarget(event.target);
     touch = event.targetTouches[0];
     if (this.deviceIsIOS) {
         selection = window.getSelection();
         if (selection.rangeCount && !selection.isCollapsed) {
             return true;
         }
         if (!this.deviceIsIOS4) {
             if (touch.identifier === this.lastTouchIdentifier) {
                 event.preventDefault();
                 return false;
             }
             this.lastTouchIdentifier = touch.identifier;
             this.updateScrollParent(targetElement);
         }
     }
     this.trackingClick = true;
     this.trackingClickStart = event.timeStamp;
     this.targetElement = targetElement;
     this.touchStartX = touch.pageX;
     this.touchStartY = touch.pageY;
     if ((event.timeStamp - this.lastClickTime) < 200) {
         event.preventDefault();
     }
     return true;
 };
로그인 후 복사

其中用到了一个方法:

FastClick.prototype.getTargetElementFromEventTarget = function (eventTarget) {
  &#39;use strict&#39;;
  if (eventTarget.nodeType === Node.TEXT_NODE) {
    return eventTarget.parentNode;
  }
  return eventTarget;
};
로그인 후 복사

他是获取我们当前touchstart的元素

然后将鼠标的信息记录了下来,他记录鼠标信息主要在后面touchend时候根据x、y判断是否为click
是ios情况下还搞了一些事情,我这里跳过去了

然后这里记录了一些事情就跳出去了,没有特别的事情,现在我们进入我们的出口touchend

 FastClick.prototype.onTouchEnd = function (event) {
     &#39;use strict&#39;;
     var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
     log(&#39;touchend&#39;);
     if (!this.trackingClick) {
         return true;
     }
     if ((event.timeStamp - this.lastClickTime) < 200) {
         this.cancelNextClick = true;
         return true;
     }
     this.lastClickTime = event.timeStamp;
     trackingClickStart = this.trackingClickStart;
     this.trackingClick = false;
     this.trackingClickStart = 0;
     if (this.deviceIsIOSWithBadTarget) {
         touch = event.changedTouches[0];
         targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
         targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
     }
     targetTagName = targetElement.tagName.toLowerCase();
     if (targetTagName === &#39;label&#39;) {
         forElement = this.findControl(targetElement);
         if (forElement) {
             this.focus(targetElement);
             if (this.deviceIsAndroid) {
                 return false;
             }
             targetElement = forElement;
         }
     } else if (this.needsFocus(targetElement)) {
         if ((event.timeStamp - trackingClickStart) > 100 || (this.deviceIsIOS && window.top !== window && targetTagName === &#39;input&#39;)) {
             this.targetElement = null;
             return false;
         }
         this.focus(targetElement);
         if (!this.deviceIsIOS4 || targetTagName !== &#39;select&#39;) {
             this.targetElement = null;
             event.preventDefault();
         }
         return false;
     }
     if (this.deviceIsIOS && !this.deviceIsIOS4) {
         scrollParent = targetElement.fastClickScrollParent;
         if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
             return true;
         }
     }
     if (!this.needsClick(targetElement)) {
         event.preventDefault();
         this.sendClick(targetElement, event);
     }
     return false;
 };
로그인 후 복사

这个家伙洋洋洒洒干了许多事情

这里纠正一个错误,他onclick那些东西现在也执行了......可能是我屏幕有变化(滑动)导致

if ((event.timeStamp - this.lastClickTime) < 200) {
 this.cancelNextClick = true;
 return true;
}
로그인 후 복사

这个代码很关键,我们首次点击会执行下面的逻辑,如果连续点击就直接完蛋,下面的逻辑丫的不执行了......
这个不执行了,那么这个劳什子又干了什么事情呢?
事实上下面就没逻辑了,意思是如果确实点击过快,两次点击只会执行一次,这个阀值为200ms,这个暂时看来是没有问题的

好了,我们继续往下走,于是我意识到又到了一个关键点
因为我们用tap事件不能使input获得焦点,但是fastclick却能获得焦点,这里也许是一个关键,我们来看看几个与获取焦点有关的函数

 FastClick.prototype.focus = function (targetElement) {
     &#39;use strict&#39;;
     var length;
     if (this.deviceIsIOS && targetElement.setSelectionRange) {
         length = targetElement.value.length;
         targetElement.setSelectionRange(length, length);
     } else {
         targetElement.focus();
     }
 };
로그인 후 복사

setSelectionRange是我们的关键,也许他是这样获取焦点的......具体我还要下来测试,留待下次处理吧
然后下面如果时间间隔过长,代码就不认为操作的是同一dom结构了

最后迎来了本次的关键:sendClick,无论是touchend还是onMouse都会汇聚到这里

 FastClick.prototype.sendClick = function (targetElement, event) {
     &#39;use strict&#39;;
     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(&#39;MouseEvents&#39;);
     clickEvent.initMouseEvent(&#39;click&#39;, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
     clickEvent.forwardedTouchEvent = true;
     targetElement.dispatchEvent(clickEvent);
 };
로그인 후 복사

他创建了一个鼠标事件,然后dispatchEvent事件(这个与fireEvent类似)

 //document上绑定自定义事件ondataavailable
 document.addEventListener(&#39;ondataavailable&#39;, function (event) {
 alert(event.eventType);
 }, false);
 var obj = document.getElementById("obj");
 //obj元素上绑定click事件
 obj.addEventListener(&#39;click&#39;, function (event) {
 alert(event.eventType);
 }, false);
 //调用document对象的 createEvent 方法得到一个event的对象实例。
 var event = document.createEvent(&#39;HTMLEvents&#39;);
 // initEvent接受3个参数:
 // 事件类型,是否冒泡,是否阻止浏览器的默认行为
 event.initEvent("ondataavailable", true, true);
 event.eventType = &#39;message&#39;;
 //触发document上绑定的自定义事件ondataavailable
 document.dispatchEvent(event);
 var event1 = document.createEvent(&#39;HTMLEvents&#39;);
 event1.initEvent("click", true, true);
 event1.eventType = &#39;message&#39;;
 //触发obj元素上绑定click事件
 document.getElementById("test").onclick = function () {
 obj.dispatchEvent(event1);
 };
로그인 후 복사

至此,我们就知道了,我们为dom先绑定了鼠标事件,然后touchend时候触发了,而至于为什么本身注册的click未触发就要回到上面代码了

解决“点透”(成果)

有了这个思路,我们来试试我们抽象出来的代码:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
     <title></title>
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
     <style>
         #list { display: block; position: absolute; top: 100px; left: 10px; width: 200px; height: 100px; }
         p { display: block; border: 1px solid black; height: 300px; width: 100%; }
         #input { width: 80px; height: 200px; display: block; }
     </style>
 </head>
 <body>
     <p>
     </p>
     <p>
         <p>
             <input type="text" />
         </p>
     </p>
     <script type="text/javascript">
         var el = null;
         function getEvent(el, e, type) {
             e = e.changedTouches[0];
             var event = document.createEvent(&#39;MouseEvents&#39;);
             event.initMouseEvent(type, true, true, window, 1, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
             event.forwardedTouchEvent = true;
             return event;
         }
         list.addEventListener(&#39;touchstart&#39;, function (e) {
             var firstTouch = e.touches[0]
             el = firstTouch.target;
             t1 = e.timeStamp;
         })
         list.addEventListener(&#39;touchend&#39;, function (e) {
             e.preventDefault();
             var event = getEvent(el, e, &#39;click&#39;);
             el.dispatchEvent(event);
         })
         var list = document.getElementById(&#39;list&#39;);
         list.addEventListener(&#39;click&#39;, function (e) {
             list.style.display = &#39;none&#39;;
             setTimeout(function () {
                 list.style.display = &#39;&#39;;
             }, 1000);
         })
     </script>
 </body>
 </html>
로그인 후 복사

这样的话,便不会点透了,这是因为zepto touch事件全部绑定值document,所以 e.preventDefault();无用
结果我们这里是直接在dom上,e.preventDefault();
便起了作用不会触发浏览器默认事件,所以也不存在点透问题了,至此点透事件告一段落......

帮助理解的图

代码在公司写的,回家后不知道图上哪里了,各位将就看吧

为什么zepto会点透/fastclick如何解决点透

我最开始就给老大说zepto处理tap事件不够好,搞了很多事情出来

因为他事件是绑定到document上,先touchstart然后touchend,根据touchstart的event参数判断该dom是否注册了tap事件,有就触发

于是问题来了,zepto的touchend这里有个event参数,我们event.preventDefault(),这里本来都是最上层了,这就代码压根没什么用

但是fastclick处理办法不可谓不巧妙,这个库直接在touchend的时候就触发了dom上的click事件而替换了本来的触发时间

意思是原来要350-400ms执行的代码突然就移到了50-100ms,然后这里虽然使用了touch事件但是touch事件是绑定到了具体dom而不是document上

그래서 e.preventDefault가 효과적입니다. 버블링과 브라우저 기본 이벤트를 방지할 수 있습니다. 이것이 바로 fastclick의 핵심입니다. 나쁘지 않습니다! ! !

Fastclick 코드 전체가 읽기에 훌륭합니다. 오늘 많은 것을 얻었습니다. 여기에 기록합니다

Postscript

위 설명에 문제가 있습니다. 수정하겠습니다.

우선, 다시 돌아가겠습니다. 원래 zepto 계획 및 내용 확인 질문:

js 표준은 탭 이벤트를 지원하지 않기 때문에 Zepto 탭은 초기화될 때 터치 이벤트를 문서에 바인딩합니다. 이벤트 매개변수에 따라 현재 요소를 저장하고 내려갈 때의 마우스 위치는 현재 요소의 마우스 이동 범위에 따라 클릭 이벤트인지 여부를 결정합니다. 그렇다면 등록된 탭 이벤트가 트리거됩니다

그러면 fastclick 처리는 기본적으로 zepto와 동일하지만 다릅니다

fastclick은 전달한 요소(보통 document.body)에 이벤트를 바인딩합니다.

② touchstart 및 touchend 이후(현재 클릭 el을 수동으로 가져옵니다) , 클릭 이벤트인 경우 dom 요소의 클릭 이벤트가 수동으로 트리거됩니다

그래서 click 이벤트는 touchend에서 트리거되고 전체 응답 속도는 실제로 zepto 탭과 동일합니다

알겠습니다. 기본적으로 동일한 코드를 사용하면 zepto는 클릭할 수 있지만 fastclick은 클릭할 수 없는 이유는 무엇입니까?

이유는 zepto의 코드에 settimeout이 있고 이 코드에서 e.preventDefault()를 실행해도 작동하지 않기 때문입니다.

이것이 근본적인 차이점입니다. 왜냐하면 settimeout의 우선순위가 낮기 때문입니다

타이머를 사용하면, 코드는 setTimeout으로 실행되며, 이 코드는 JS 엔진의 끝에 배치됩니다.

그리고 우리 코드는 e.preventDefault를 즉시 감지합니다. settimeout이 추가되면 e.preventDefault가 적용되지 않습니다. 문제의 근본 원인

결론

위 내용은 앞으로 모든 사람에게 도움이 되기를 바랍니다.

관련 기사:

GM 자르기를 사용하여 Nodejs에서 이미지 합성

js를 사용하여 json을 호출하는 방법

Puppeteer 이미지 인식 기술을 사용하여 Baidu 인덱스 크롤러를 구현하는 방법

위 내용은 fastclick 코드에서 탭 '클릭 연결'을 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

프론트 엔드 열 용지 영수증에 대한 차량 코드 인쇄를 만나면 어떻게해야합니까? 프론트 엔드 열 용지 영수증에 대한 차량 코드 인쇄를 만나면 어떻게해야합니까? Apr 04, 2025 pm 02:42 PM

프론트 엔드 개발시 프론트 엔드 열지대 티켓 인쇄를위한 자주 묻는 질문과 솔루션, 티켓 인쇄는 일반적인 요구 사항입니다. 그러나 많은 개발자들이 구현하고 있습니다 ...

Demystifying JavaScript : 그것이하는 일과 중요한 이유 Demystifying JavaScript : 그것이하는 일과 중요한 이유 Apr 09, 2025 am 12:07 AM

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

누가 더 많은 파이썬이나 자바 스크립트를 지불합니까? 누가 더 많은 파이썬이나 자바 스크립트를 지불합니까? Apr 04, 2025 am 12:09 AM

기술 및 산업 요구에 따라 Python 및 JavaScript 개발자에 대한 절대 급여는 없습니다. 1. 파이썬은 데이터 과학 및 기계 학습에서 더 많은 비용을 지불 할 수 있습니다. 2. JavaScript는 프론트 엔드 및 풀 스택 개발에 큰 수요가 있으며 급여도 상당합니다. 3. 영향 요인에는 경험, 지리적 위치, 회사 규모 및 특정 기술이 포함됩니다.

JavaScript를 사용하여 동일한 ID와 동일한 ID로 배열 요소를 하나의 객체로 병합하는 방법은 무엇입니까? JavaScript를 사용하여 동일한 ID와 동일한 ID로 배열 요소를 하나의 객체로 병합하는 방법은 무엇입니까? Apr 04, 2025 pm 05:09 PM

동일한 ID로 배열 요소를 JavaScript의 하나의 객체로 병합하는 방법은 무엇입니까? 데이터를 처리 할 때 종종 동일한 ID를 가질 필요가 있습니다 ...

JavaScript는 배우기가 어렵습니까? JavaScript는 배우기가 어렵습니까? Apr 03, 2025 am 12:20 AM

JavaScript를 배우는 것은 어렵지 않지만 어려운 일입니다. 1) 변수, 데이터 유형, 기능 등과 같은 기본 개념을 이해합니다. 2) 마스터 비동기 프로그래밍 및 이벤트 루프를 통해이를 구현하십시오. 3) DOM 운영을 사용하고 비동기 요청을 처리합니다. 4) 일반적인 실수를 피하고 디버깅 기술을 사용하십시오. 5) 성능을 최적화하고 모범 사례를 따르십시오.

Shiseido의 공식 웹 사이트와 같은 시차 스크롤 및 요소 애니메이션 효과를 달성하는 방법은 무엇입니까?
또는:
Shiseido의 공식 웹 사이트와 같은 페이지 스크롤과 함께 애니메이션 효과를 어떻게 달성 할 수 있습니까? Shiseido의 공식 웹 사이트와 같은 시차 스크롤 및 요소 애니메이션 효과를 달성하는 방법은 무엇입니까? 또는: Shiseido의 공식 웹 사이트와 같은 페이지 스크롤과 함께 애니메이션 효과를 어떻게 달성 할 수 있습니까? Apr 04, 2025 pm 05:36 PM

이 기사에서 시차 스크롤 및 요소 애니메이션 효과 실현에 대한 토론은 Shiseido 공식 웹 사이트 (https://www.shiseido.co.jp/sb/wonderland/)와 유사하게 달성하는 방법을 살펴볼 것입니다.

Console.log 출력 결과의 차이 : 두 통화가 다른 이유는 무엇입니까? Console.log 출력 결과의 차이 : 두 통화가 다른 이유는 무엇입니까? Apr 04, 2025 pm 05:12 PM

Console.log 출력의 차이의 근본 원인에 대한 심층적 인 논의. 이 기사에서는 Console.log 함수의 출력 결과의 차이점을 코드에서 분석하고 그에 따른 이유를 설명합니다. � ...

프론트 엔드 개발에서 VSCODE와 유사한 패널 드래그 앤 드롭 조정 기능을 구현하는 방법은 무엇입니까? 프론트 엔드 개발에서 VSCODE와 유사한 패널 드래그 앤 드롭 조정 기능을 구현하는 방법은 무엇입니까? Apr 04, 2025 pm 02:06 PM

프론트 엔드에서 VSCODE와 같은 패널 드래그 앤 드롭 조정 기능의 구현을 탐색하십시오. 프론트 엔드 개발에서 VSCODE와 같은 구현 방법 ...

See all articles