点透 & 解决方案

学习map:

  • 现象:再现现象,总结导致点透出现的情况
  • 分析原因
  • 解决办法

现象

再现点透现象请使用一下方式:

  1. 手机访问传送门
  2. 复制链接到连图生成二维码后扫一扫
  3. 或者打开chrome手机视图并打开touch screen

zepto-隐藏元素-点透

代码:

zepto-最新-没有点透

代码:

原生js-隐藏元素-点透

代码:

原生js-元素出现-点透

代码:

综上,导致点透现象出现的场景:

  1. 元素z轴重叠;
  2. 绑定了touch事件的元素消失或移走;
  3. 绑定有类click事件的元素出现在点击区域;

PS:

a链接的href跳转、input、select等表单元素的聚焦并弹起软键盘,等触发的事件和click一样,由于历史原因在手机端也会表现出300ms延迟,因此也都可以看做是click事件,我叫他类click事件,demo可用爪机狠戳事件触发顺序&click延迟demo或者复制链接到连图生成二维码后扫一扫。类click事件,也是一种浏览器默认行为,可被event.preventDefault()阻止。

代码:

分析

事件触发顺序&click延迟demo这个demo可以看出:

  • iphone系手机:mouse事件在touchend之后才开始,mouse事件覆盖了click事件,屏蔽了input弹出键盘的默认行为
  • 安卓手机:mouse事件会先于touch事件开始,而迟于其结束,mouse事件没有覆盖input的聚焦弹软键盘的浏览器默认行为
  • 在ios设备上的浏览器[UC浏览器除外]能明显感受到click延迟
  • 无论是android还是ios还是PC的touch screen的click(or mouse)事件都是迟于touchend事件被触发的
  • 因此,从手指触摸屏幕到离开屏幕,先后触发了touchstarttouchendclick

由于我们在touchend阶段z轴层级已经发生了变化,当click被触发时候,能够被点击的元素则是当前z轴离用户最近的层,根据click事件的触发规则:

在被触发时,当前有绑定click事件的元素显示,且在面朝用户的最前端时,才触发click事件

因此touchend之后符合条件的绑定了click事件的元素被点透。

总而言之:出现点透是由于移动端click事件迟于touch事件被触发导致的。

解决办法

不优雅的办法:

1. 对于默认绑定了类click事件的元素(如a、input、select等)
  • touchend + preventDefault及时取消touch元素的默认click事件,即if(eve == "touchend") e.preventDefault();

  • 如果牺牲点性能无所谓的话,可以将可能在z轴方向上引起点透现象的元素绑定成click事件,比如遮罩层之类的,不过还可以增加些许有趣的交互抵消用户的焦躁心理,比如:demo-ripple

2. 对于没有默认绑定类click事件的元素

统一使用touch事件。z轴上都绑定touchstarttouchendtap不用阻止默认行为也不会穿透。

使用上述方法有很明显的缺点和不方便:

使用touchend + preventDefault要在同一个元素上绑定2个事件,zepto可以封装成tap事件,我们也可以,自定义tap事件阻止点透

代码:

在本demo中,虽然没有出现点透现象,但是点击出现弹层以后你会发现点击a链接、span、input都没有任何反映了,这是因为在touchend里阻止浏览器默认行为,触发自定义tap事件,不仅会阻止掉了input的软键盘弹出,还会阻止一切非tap事件,解决办法就是使用合成的click事件去覆盖会延迟的click事件。

重写click事件

代码:

event.initEvent('click', bubbles, true);

touch_target.addEventListener("click", handle, false);

这样再次点击,弹层上的a链接和span的click事件都响应地很迅速,然而input和select的弹出软键盘的功能被阉割了,实际上input弹出软键盘的事件是focus()事件

3. fastclick

读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吧。

4. tap.js

tap.js源码只有不到200行,大致看了下,并不能解决类click的延迟问题,鸡肋!

点透 & 解决方案的更多相关文章

  1. Android Fragment 多层叠加时点击穿透解决方案

    一.问题现象 多层fragment叠加时,点击上层fragment会使下层fragment的控件对应点击事件响应,这种现象就是点击穿透. 对于这种情况,我们一般都是对baseFragment进行vie ...

  2. 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

    前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...

  3. 面试题HTML +CSS

    HTML+CSS部分1.行内元素和块级元素?img算什么?行内元素怎么转化为块级元素?行内元素:和有他元素都在一行上,高度.行高及外边距和内边距都不可改变,文字图片的宽度不可改变,只能容纳文本或者其他 ...

  4. Fragment 点击事件的穿透和重叠bug

    从A fragment跳转到B fragment ,为了返回时不从新加载A fragment内容,通常使用add方法来将a添加到后退栈. 在B Fragment 中点击一个空白区域,如果A Fragm ...

  5. 前端基础面试题(JS部分)

    1.几种基本数据类型?复杂数据类型?值类型和引用数据类型?堆栈数据结构? 基本数据类型:Undefined.Null.Boolean.Number.String 值类型:数值.布尔值.null.und ...

  6. 移动端的300ms延迟和点击穿透

    移动端300ms延迟:假定这么一个场景.用户在 浏览器里边点击了一个链接.由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行 ...

  7. 前端基础面试题(js部分)

      前端基础面试题(JS部分)   1.几种基本数据类型?复杂数据类型?值类型和引用数据类型?堆栈数据结构? 基本数据类型:Undefined.Null.Boolean.Number.String值类 ...

  8. zepto之tap事件点透问题分析及解决方案

    点透现象出现的场景: 当A/B两个层上下z轴重叠,上层的A点击后消失或移开(这一点很重要),并且B元素本身有默认click事件(如a标签)或绑定了click事件.在这种情况下,点击A/B重叠的部分,就 ...

  9. zepto的tap事件的点透问题的几种解决方案

    你可能碰到过在页面上创建一个弹出层,弹出层有个关闭的按钮,你点了这个按钮关闭弹出层后,这个按钮正下方的内容也会执行点击事件(或打开链接).这个被定义为这是一个“点透”现象. 以前,我也听到过tap的点 ...

随机推荐

  1. APP远程调试及网络自动化测试

    一:优测 腾讯旗下的测试服务 http://utest.qq.com/ 二:云测 http://www.testin.cn/ 三:testbird 1.进入这个网站,注册并且登录 https://dt ...

  2. 【代码笔记】iOS-翻页效果的实现

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...

  3. iOS中View生命周期与布局

    生命周期 说到view的生命周期一般都是指视图控制器的view生命周期. view的创建:loadView 视图控制器(UIViewController)及其子类,无论是手写代码还是storyboar ...

  4. git 错误:

    git  错误: $ git commit -afatal: Unable to create 'e:/git/Android/XXXXXX/.git/index.lock': File exists ...

  5. django 操作 下载 excel xls xlsx csv

    网站开发离不开数据的导入导出,本文将介绍一下django如何操作excel 先安装 django-excel pip install django-excel 配置一下url url(r'^downl ...

  6. Helpful Tool

    Remote Connectivity Analyzer(Online) https://testconnectivity.microsoft.com/ https://technet.microso ...

  7. 天书笔记:如何创建一个现代的footer(页脚)

    此笔记纯属本人脑残笔记,阅读困难不理解属正常现象,初学者尽量不要阅读,否则轻则口吐白沫重则走火入魔,切记切记 老规矩,效果图 这个布局也是从b站看到的,回来自己实现了一遍 HTML: <div ...

  8. 让你脱离google不能访问的烦恼

    大陆封了google已有20多天了,给开发者带来了许多不便.只需两步让你的google可以使用: 1.设置hosts: 访问:https://git.oschina.net/kawaiiushio/m ...

  9. javascript多态 - 类形式实现demo

    /* *多态 * 对传入的参数做判断以实现多种调用方式 */ //类形式实现 function Add(){ function zero(){ return 10; } function one(nu ...

  10. 编写IoDemo.java的Java应用程序,程序完成的功能是:首先读取text.txt文件内容,再通过键盘输入文件的名称为iodemo.txt,把text.txt的内容存入iodemo.txt

    package zuoye; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcep ...