点透 & 解决方案

学习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]使用Gradle提交自己开源Android库到Maven中心库

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4388175.html 此文针对开源爱好者. 如果你想让别人使用 ...

  2. mysql 时间函数转换

    1 NOW() //当前时间 2 SYSDATE() //当前时间 3 CURRENT_TIMESTAMP 4 以'YYYY-MM-DD HH:MM:SS'或YYYYMMDDHHMMSS格式返回当前的 ...

  3. Android studio打开项目时出现 gradle download 无反应

    在使用android studio 新建 项目的时候,会发现一直无法下载 gradle程序一直卡住不动,原因是被墙了. 解决方法: 到gradle官网下载离线包,解压后将bin路径加入Path环境变量 ...

  4. iOS设置文字过长时的显示格式

    以label为例: //设置文字过长时的显示格式 aLabel.lineBreakMode = UILineBreakModeMiddleTruncation; //截去中间 aLabel.lineB ...

  5. 【iOS】WebView加载HTML图片大小自适应与文章自动换行

    在很多App中都会使用到webview,尤其是在加载新闻内容等文章形式的数据时.因为图文混编以及不同字体格式的显示,在iOS进行编辑 和显示都是一大问题(当然,iOS中也可以用CoreText进行绘制 ...

  6. 创建一个Android项目

    当我们的eclipse安装了SDK后,点击Window-->Perference-->DDMS.eclipse界面立即转为DDMS界面. 这时,我们可以打开我们的服务端(安卓模拟器或者是我 ...

  7. iOS做新浪微博sso授权登录遇到的一些坑

    新浪微博sso授权第三方登录,这里没有借助第三方框架,如shareSKD和友盟等,直接参考新浪官方SDK和文档. 过程中遇到几个坑,找了很久,好歹最后解决了,记录如下 问题1: _NSInlineDa ...

  8. php curl 发送 json 数据

    $urlcon= 'http://localhost/******.php'; $data=' { "button":[ { "type":"clic ...

  9. 常见HTTP状态码

    常见HTTP状态码 200 OK 301 Moved Permanently 302 Found 304 Not Modified 307 Temporary Redirect 400 Bad Req ...

  10. Struts

    Struts是基于MVC的框架,它进一步的对MVC进行了封装. MVC 概念 MVC全名是Model View Controller,是模型(model)—视图(view)—控制器(controlle ...