点透 & 解决方案前端
学习map:node
再现点透现象请使用一下方式:android
touch screen
。代码:ios
代码:git
代码:github
代码:chrome
综上,致使点透现象出现的场景:浏览器
PS:
a连接的href跳转、input、select等表单元素的聚焦并弹起软键盘,等触发的事件和click同样,因为历史缘由在手机端也会表现出300ms延迟,所以也均可以看作是click事件,我叫他类click事件,demo可用爪机狠戳事件触发顺序&click延迟demo或者复制连接到连图生成二维码后扫一扫。类click事件,也是一种浏览器默认行为,可被event.preventDefault()阻止。iphone
代码:函数
从事件触发顺序&click延迟demo这个demo能够看出:
android
仍是ios
仍是PC的touch screen
的click(or mouse)事件都是迟于touchend
事件被触发的touchstart
、touchend
、click
因为咱们在touchend
阶段z轴层级已经发生了变化,当click被触发时候,可以被点击的元素则是当前z轴离用户最近的层,根据click事件的触发规则:
在被触发时,当前有绑定click事件的元素显示,且在面朝用户的最前端时,才触发click事件
所以touchend以后符合条件的绑定了click事件的元素被点透。
总而言之:出现点透是因为移动端click事件迟于touch事件被触发致使的。
touchend + preventDefault
及时取消touch元素的默认click事件,即if(eve == "touchend") e.preventDefault();
。
若是牺牲点性能无所谓的话,能够将可能在z轴方向上引发点透现象的元素绑定成click事件,好比遮罩层之类的,不过还能够增长些许有趣的交互抵消用户的焦躁心理,好比:demo-ripple。
统一使用touch事件。z轴上都绑定touchstart
和 touchend
、 tap
不用阻止默认行为也不会穿透。
使用上述方法有很明显的缺点和不方便:
使用touchend + preventDefault
要在同一个元素上绑定2个事件,zepto能够封装成tap事件,咱们也能够,自定义tap事件阻止点透
代码:
在本demo中,虽然没有出现点透现象,可是点击出现弹层之后你会发现点击a连接、span、input都没有任何反映了,这是由于在touchend里阻止浏览器默认行为,触发自定义tap事件,不只会阻止掉了input的软键盘弹出,还会阻止一切非tap事件,解决办法就是使用合成的click事件去覆盖会延迟的click事件。
代码:
event.initEvent('click', bubbles, true); touch_target.addEventListener("click", handle, false);
这样再次点击,弹层上的a连接和span的click事件都响应地很迅速,然而input和select的弹出软键盘的功能被阉割了,实际上input弹出软键盘的事件是focus()事件
读fastclick源码
layer.removeEventListener('touchend', this.onTouchEnd, false); FastClick.prototype.onTouchEnd = function(event) { ... targetTagName = targetElement.tagName.toLowerCase(); if (targetTagName === 'label') { forElement = this.findControl(targetElement); if (forElement) { this.focus(targetElement); if (deviceIsAndroid) { return false; } targetElement = forElement; } } else if (this.needsFocus(targetElement)) { if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) { this.targetElement = null; return false; } this.focus(targetElement); this.sendClick(targetElement, event); if (!deviceIsIOS || targetTagName !== 'select') { this.targetElement = null; event.preventDefault(); } return false; } // needsFocus FastClick.prototype.needsFocus = function(target) { switch (target.nodeName.toLowerCase()) { case 'textarea': return true; case 'select': return !deviceIsAndroid; case 'input': switch (target.type) { case 'button': case 'checkbox': case 'file': case 'image': case 'radio': case 'submit': return false; } // No point in attempting to focus disabled inputs return !target.disabled && !target.readOnly; default: return (/\bneedsfocus\b/).test(target.className); } }; // sendClick 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); };
上面代码的意思就是:在目标元素
上绑定touchend事件,在事件处理函数里,若是是须要focus的表单元素被点击,则先触发他们的focus事件,再触发自定义的click事件,fastclick之因此大,就是由于对不少表单元素在各个系统的各个版本的不一样表现作了兼容,不只解决了click以及类click的延迟问题,并且当检测到当前页面使用了基于 <meta>
标签或者 touch-action
属性的解决方案时,会静默退出。能够说,这是真正的跨平台方案出来以前一种很好的变通方案。而zepto
只是为普通的点击事件封装了一个更快的tap事件,类click事件的延迟问题并无获得解决,并且移动端使用的tap事件,若是没作设备判断兼容PC的话,PC端的点击事件将得不到响应,这会很影响网站的可用性和可访问性。不过zepto封装了一系列移动端很须要的功能,好比swipeLeft、swipeRight、swipeUp、等等,两者各有春秋,兼并二者优点的库我目前没遇到,不过能够尝试本身写一个,加个todo吧。
tap.js源码只有不到200行,大体看了下,并不能解决类click的延迟问题,鸡肋!