点透 & 解决方案

学习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. [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注解支持

    以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3709957.html 之前写过一篇博客,是使用Fragment来实现T ...

  2. SQL for SQLite

    语法 verb + subject + predicate commannds(命令) SQL由命令组成,以分号为结束.命令有token组成,token由white space分隔,包括空格.tab. ...

  3. 【代码笔记】iOS-改变导航条标题的颜色为红色

    一,效果图. 二,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional se ...

  4. 【代码笔记】iOS-点击一个button,出6个button

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> //加入头文件 #import "DCPathB ...

  5. GCD基础知识总结

    iOS三种多线程编程技术: 1.NSThread 2.NSOperation 3.GCD(Grand Central Dispatch) 从上到下,抽象度层次从低到高,抽象度越高的使用越简单,也是Ap ...

  6. mac 终端 常用指令

    开始正式研究ios 应用开发,由于是从C开始学起,所以学习下常用的mac终端指令,方便后续常用操作. mac 终端 常用指令: 1.ls指令 用途:列出文件 常用参数 -w 以简洁的形式列出所有文件和 ...

  7. 史上最详细“截图”搭建Hexo博客并部署到Github

    http://jingyan.baidu.com/article/d8072ac47aca0fec95cefd2d.html 大家也搭建过博客,很多时候,按着教程来做就可以了,但是我当时为了搭建Hex ...

  8. C#生成注册码

    string t = DateTime.Now.Ticks.ToString(); t = DESKey.DESEncrypt(t, DESKey.DesKeyStr); string[] strid ...

  9. Play Framework 第一个应用

    熟悉的Hello World 新创建一个工程,了解下重要文件的结构 .\app controllers\models\views 目前比较流行的MVC架构 .\conf application.con ...

  10. Virtual Box 杂记

    1. Virtual Box后台运行 a. VBoxManage startvm yourvmname --type headlessb. VBoxHeadless --startvm yourvmn ...