fastclick 源码注解及一些基础知识点
副标题[/!--empirenews.page--]
在移动端,网页上的点击穿透问题导致了非常糟糕的用户体验。那么该如何解决这个问题呢? 问题产生的原因 移动端浏览器的点击事件存在300ms的延迟执行,这个延迟是由于移动端需要通过在这个时间段用户是否两次触摸屏幕而触发放大屏幕的功能。那么由于click事件将延迟300ms的存在,开发者在页面上做一些交互的时候往往会导致点击穿透问题(可以能是层之间的,也可以是页面之间的)。 解决问题 之前遇到这个问题的时候,有在网上看了一些关于解决移动端点击穿透的问题,也跟着网上提出的方式进行了各项测试,最终还是觉得使用fastclick插件比较靠谱些,其他几种方法多多少少会存在一些其他问题(当然,fastclick也不是说完全兼容各项,但相对于其他一些方法不会造成较明显的问题) 使用方式: <!-- 引入文件 --> <script src="fastclick.js"></script> js: // fastclick 使用 /* @params layer 需要处理click事件的视图 @params options 一些配置 { touchBoundary: 10 // 点击事件边界线 tapDelay: 200 // tap最小延时 tapTimeout: 700 // tap最大延时 } */ FastClick.attach(layer,options) // 一般使用 FastClick.attach(document.body) fastclick实现过程 首先,扔上注解文件:fastclick-read.js 中文注解文件 ,下面内容提取部分代码 1.拦截给定视图区域的各项事件,绑到layer处理 // Set up event handlers as required // 配置需要用到的操作事件/给指定绑定各项事件监听 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); 2.判断是否需要对触发事件的标签生成一个针对该标签的click事件
![]() // onTouchStart 代码行 // 注册click事件追踪 this.trackingClick = true; this.trackingClickStart = event.timeStamp; this.targetElement = targetElement; this.touchStartX = touch.pageX; this.touchStartY = touch.pageY; // 若干行代码... // 防止快速的两次tap导致的点透 if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { event.preventDefault(); } // onTouchMove 代码行 // 如果touch事件是移动的,取消点击事件跟踪 if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) { this.trackingClick = false; this.targetElement = null; } // onTouchEnd 代码行 // 阻止快速双击导致触发第二次屏幕点击事件 (issue #36) if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { this.cancelNextClick = true; return true; } // 如果超出最大延时,事件继续执行 if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) { return true; } // 重新设置cancelNextClick,阻止input的事件被某些异常所取消 (issue #156) this.cancelNextClick = false; this.lastClickTime = event.timeStamp; trackingClickStart = this.trackingClickStart; this.trackingClick = false; this.trackingClickStart = 0; // 舍去一些兼容处理的代码 // else if (this.needsFocus(targetElement)) 判断且满足needsFocus条件 如果点击元素是为了聚焦 this.focus(targetElement); this.sendClick(targetElement, event); // 这里生成click // 判断且满足needsClick条件 如果点击元素是为了点击 // 阻止真实的点击继续执行 -- 除非该标签被标记为允许真实点击(class="needsclick") if (!this.needsClick(targetElement)) { event.preventDefault(); this.sendClick(targetElement, event); // 这里生成click } // onTouchCancel 代码行 FastClick.prototype.onTouchCancel = function() { this.trackingClick = false; this.targetElement = null; }; // onClick 代码行 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. // 点击事件在被fastclick触发之前已经被其他类似fastclick功能的第三方代码库触发的情况下,尽早的为事件跟踪标签返回一个false值,同时也能够尽早结束onTouchEnd事件 if (this.trackingClick) { 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. // IOS的异常现象(issue #18) : 当表单内存在submit按钮,在ios模拟器点击"enter"或者在弹出键盘中点击"go",将会触发一次"伪装"成submit按钮的点击事件将表单提交 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. // 如果不允许click,那么只设置targetElement。确保onMouse中!targetElement的判断结果有值,并且浏览器的点击失效 if (!permitted) { this.targetElement = null; } // If clicks are permitted, return true for the action to go through. // 如果允许click,返回true用以点击动作的传递 return permitted; }; // onMouse 代码行 FastClick.prototype.onMouse = function(event) { // If a target element was never set (because a touch event was never fired) allow the event // 如果不存在targetElement(触摸事件未被触发),返回true if (!this.targetElement) { return true; } if (event.forwardedTouchEvent) { return true; } // Programmatically generated events targeting a specific element should be permitted // 代码触发的事件,并且针对有明确的元素,则返回true 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. // 检查目标元素,确定鼠标事件是否需要被允许 // 除非明确指定事件可行,不然就阻止非点击事件,主要用于元素重叠情况下双击产生异常而触发不必要的事件。 if (!this.needsClick(this.targetElement) || this.cancelNextClick) { // Prevent any user-added listeners declared on FastClick element from being fired. // 阻止fastclick元素上其他的定义的事件被触发 if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2) // hack 为了一些不支持Event#stopImmediatePropagation的客户端(如 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. // 如果允许该鼠标事件,返回true用以点击动作的传递 return true; }; // sendClick 代码行 -- 生成click事件 // 模拟一次点击事件,同时添加上forwardedTouchEvent,表明可被跟踪 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);View Code
以上就是重点的一些判断及实现的代码,可点击"github - fastclick-read 源码注释"理解更详细内容,建议动手测试,完整的跟一次click事件在fastclick代码内的执行流程。 源码中一些挺实用的基础知识点 事件冒泡和事件捕获 事件冒泡:事件在某个节点被触发,将会随着DOM树向上冒泡并根据当前节点是否满足冒泡触发的条件来进行同类型事件的触发,直至根节点(html)。 事件捕获:事件在根节点上被触发,开始向子元素传播并根据当前节点是否满足捕获触发的条件来进行同类型事件的触发,直至实际触发该事件的节点。 (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |