移动端点击事件全攻略

  接触过移动端开发的同学可能都会面临点击事件的第一个问题:click事件的300ms延迟响应。不能立即响应给体验造成了很大的困扰,因此解决这个问题就成为了必然。

  这个问题的解决方案就是:zepto.js的tap事件。tap事件可以理解为在移动端的click事件,而zepto.js因为几乎完全复制jQuery的api,因此常常被用在h5的开发上用来取代jquery.

由于模块化的原因,导致有的同学下载的zepto.js的模块并不全,造成了一大堆悲剧,以为zepto.js不支持一些方法,所以,下载的时候注意一点。

  然而事情到这里并没有结束,因为tap事件解决了一个300ms延迟问题,却带来了一个新的重大bug,点击穿透

  点击穿透的意思,就是如果一个绝对定位或者固定定位元素处于页面最顶层,对这个元素绑定一个点击事件,那么你点击这个点对应的下面凡是有点击事件或者a标签都会被触发执行。这里就不贴图了,自行脑补各种弹窗,这种情况还是非常多的。

  为了解决这个问题,有的人试图用touchend来搞定。touchend会在手指离开手机屏幕时触发一次。没有300ms延迟,没有点击穿透,看上去简直就是完美的解决方案。可是!

先在这里简单总结一个小知识点。

    移动端有touchstart, touchmove, touchend, 以及tap
    pc端有mousedown, mousemove, mouseup, click。

  他们的关系和作用几乎可以对应起来,分别表示按下,滑动,松开。pc端可以用前面三个事件实现拖拽,移动端也可以用前面三个事件实现的滑动。

  所以,在pc端不用mouseup来替换click,就是因为他在松开鼠标的时候就会触发,导致如果我在很远的区域滑动到目标元素,然后松开,这样的情况也会触发mouseup与touchend事件。所以这种情况下是不符合点击事件的定义的。而且如果你在某种情况下,对某一个事件需要同时绑定拖拽和点击,就更加没办法解决了。

  另外还有一个很重要的原因导致touchend不能用来替换点击,是因为PC端不支持。老板们常常希望自己的页面不仅仅能够在移动端展示,因此还得想其他办法。

  我知道有经验的同学读这篇文章的时候,早就在想fastclick.js了。是的,目前来看,这是一个非常好的解决方案。为了解决300ms延迟的问题,zepto.js给出了tap事件替换的方案,而fastclick.js则是在想办法让click事件的延迟消除。因此任然是使用click事件,也就不会有点击穿透的问题。

首先想办法引入fastclick.js

<script type=‘application/javascript‘ src=‘/path/to/fastclick.js‘></script>

如果你使用原生js开发则进行如下声明即可。

if (‘addEventListener‘ in document) {
    document.addEventListener(‘DOMContentLoaded‘, function() {
        FastClick.attach(document.body);
    }, false);
}

  如果你想使用jquery

$(function() {
    FastClick.attach(document.body);
});

  如果你在使用CommonJS风格的框架,比如requirejs

var attachFastClick = require(‘fastclick‘);
attachFastClick(document.body);

AMD

var FastClick = require(‘fastclick‘);
FastClick.attach(document.body, options);

  进行对应的声明之后,你就可以在移动端页面中放心大胆的使用click事件了。说到这里,就会有一个关于zepto.js与jquery选择的问题。说起来又可以写一大篇文章了,简单来说就是,实际开发中你就会发现zepto还是有点不太爽,虽然体积小点,既然click延迟的问题已经解决了,我还是更偏向于使用jquery.

当然,我们要踩的坑并没有结束 - -!

  最近开发了一个小页面,财经日历,在日历部分,我需要同时给代表每一天的元素事件实现获取当天资讯的需求,又要给整个日历部分实现能够左右滑动来选取上一月和下一月的功能。所以我需要同时对日历部分绑定click事件和实现滑动的touchstart,touchmove,touchend事件。

  这个时候问题出现了。在安卓手机上,对同一个元素,如果我绑定了click事件,然后在绑定touchstart事件,click事件会处于几乎失效的状态。就算用了fastclick事件也无法避免这个问题

错误演示大概如下

$area.on(‘click‘, ‘.weeknumber‘, function() {
    // 点击每一天获取当天资讯
})

// 实现左右滑动
$area.on(‘touchstart‘, function() {})
.on(‘touchmove‘, function() {})
.on(‘touchend‘, function() {})

  动手能力强的同学可以去试试这个坑,这种情况下,fastfclick肯定是没办法解决的。怎么办?

  我的第一次尝试,是在当滑动距离为0的时候,运行点击事件里面的内容。我们知道在实现滑动[不知道如何实现的同学,是时候关注我的公众号了,搜索isreact找到我]的时候,会计算一个滑动距离。

 1 // 实现滑动的大概代码
 2
 3 // 滑动元素translateX的初始值
 4 var iscroll = device_width,
 5
 6     // 用来计算的中间值
 7     istarX = 0,
 8
 9     // 手指第一次点在屏幕上的x坐标
10     istart_pageX = 0;
11
12 // 绑定事件
13 $area.on(‘touchstart‘, touchstart)
14      .on(‘touchmove‘, touchmove)
15      .on(‘touchend‘, touchend);
16
17 function touchstart(event) {
18     event.preventDefault();
19     istartX = iscroll;
20     istart_pageX = event.originalEvent.changedTouches[0].pageX;
21 }
22
23 function touchmove(event) {
24
25     // 滑动过程中手指位置x坐标会不停变动,这里会保存一个当前位置与初始位置的一个差值
26     var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
27     iscroll = istartX + distance;
28
29     // 这里是我自定义的一个css方法,用来设置元素translateX的当前值
30     Utils.css(area, { translateX: iscroll });
31 }
32
33 function touchend(event) {
34     var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
35     $area.off(‘touchstart touchmove touchend‘);
36
37     // 根据差值的不同,执行不同的动作
38     if (distance < -80) {
39         slideNext(function() {
40             addEventSlider($area);
41         })
42     }
43     else if (distance > 80) {
44         slidePrev(function() {
45             addEventSlider($area);
46         })
47     }
48     else if (distance == 0) {
49         /* 当差值为0时,我认为这是执行了一次点击 */
50
51         addEventSlider($area);
52     }
53     else {
54         ani(area).animate(400, ‘easeout‘, { x: -device_width }, function() {
55             iscroll = -device_width;
56             addEventSlider($area);
57         })
58     }
59 }

上面就是我的滑动功能的实现,中间会有一些自定义的方法,因此没办法你们直接就复制过去运行。但是原理已经讲得还算明白,可以自己尝试一些简单的实现。关注我公众号会有更加详细的讲解哦!

我的这一次尝试,就是在滑动差值distance为0的时候,认为这是一次点击,因此执行点击事件应该有的动作。本来我以为这就能够解决了,测试的时候也通过了。但是 - -!产品同学非常有执念的在日历上点击了50多下,结果发现,多次点击之后,在点就失去效果了!!!

当发现这个bug的时候,我的内心是崩溃的。好吧,只能说,试图用touchend来代替点击事件的想法终究还是有一点不成熟。硬着头皮想办法继续解决上面的问题。再三思考各种解决方案,最终决定自己封装一个tap事件。封装的tap事件的代码如下。

http://yangbo5207.github.io/static/demo/single-instance/js/dev/tap.js

  这是对jquery事件的一个拓展,让jquery也能够使用tap事件。放在jquery后面引入就能够立即使用了。除此之外还扩展了longTap,swipe两个事件。

使用方式和其他一样。

$area.on(‘tap‘, function() {});

$area.tap(function() {})

  当然我封装的这个tap,任然有避免不了的点击穿透bug,所以,最终的解决方案是:对于需要同时绑定点击事件和滑动事件的元素,用tap事件,其他情况都用click事件即可。需要结合我的tap.js与fastclick.js来完美解决这个问题。心累啊,终于是搞定了。

OK,关于移动端的点击事件总结完了,可能你都没想到一个简单的点击事件会有那么多坑,如果你在工作中可能会涉及到移动端开发的话,相信这篇文章还是值得你点赞和收藏的,毕竟是踩了那么多坑的经验总结。

时间: 2024-10-12 23:12:47

移动端点击事件全攻略的相关文章

移动端点击事件全攻略,有你知道与不知道的各种坑

看标题的时候你可能会想,点击事件有什么好说的,还写一篇攻略?哈哈,如果你这么想,只能说明你too young to simple. 接触过移动端开发的同学可能都会面临点击事件的第一个问题:click事件的300ms延迟响应.不能立即响应给体验造成了很大的困扰,因此解决这个问题就成为了必然. 这个问题的解决方案就是: zepto.js的tap事件.tap事件可以理解为在移动端的click事件,而zepto.js因为几乎完全复制jQuery的api,因此常常被用在h5的开发上用来取代jquery.

webBrowser中操作网页元素全攻略

webBrowser中操作网页元素全攻略 2012-12-20 14:21 188人阅读 评论(0) 收藏 举报 1.获取非input控件的值: webBrowser1.Document.All["控件ID"].InnerText; 或webBrowser1.Document.GetElementById("控件ID").InnerText; 或webBrowser1.Document.GetElementById("控件ID").GetAttr

Angularjs中UI Router全攻略

摘自:Angularjs中UI Router全攻略 温馨提示:想要了解 angular-ui-router的同学,从上往下读一遍,能带随着coding那就更好了,保证你对angular-ui-router基本全部掌握. 如何引用依赖angular-ui-router angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateName, stateCofi

活水渠 - 云影院之云时代看片全攻略

本文讨论了大数据时代最热门的两大应用之一的云计算(另一应用是物联网)对网络视频观看体验的具体影响,以及在观看方式上区别于传统下载方式的优点. 一.视频门户类网站 此类网站以优酷.乐视等为代表,主营传统网络视频业务,使用浏览器进行直接点击链接进行观看. 优点:电视节目丰富.观看技术手段简单.大型网站服务器稳定 缺点:看不了热门电影,精华视频要收费,免费用户要看广告 解决:浏览器辅助工具或插件解决收费和广告问题,在此基础上还诞生了视频门户网 站的入口集成网站,每天看云帆可免VIP直接观看主流视频门户

fiddler Android下https抓包全攻略

fiddler Android下https抓包全攻略 fiddler的http.https的抓包功能非常强大,可非常便捷得对包进行断点跟踪和回放,但是普通的配置对于像招商银行.支付宝.陌陌这样的APP是抓不到包的,需要一些特殊的配置,本文把fiddler Android下https抓包的详细配置都罗列出来,供大家参考. 一.普通https抓包设置 先对Fiddler进行设置: 勾选“CaptureHTTPS CONNECTs”,接着勾选“Decrypt HTTPS traffic”.同时,由于我

Windows Socket五种I/O模型——代码全攻略(转)

Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权.这种模式使用 起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误.但功能强大.为了解决这个问题,提出了进行I/O操作的一些I/O模型,下面介绍最常见的三种: Windows Socket五种I/O模型——代码全攻

Tomcat全攻略

tomcat全攻略 1.tomcat是什么? Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,它早期的名称为catalina,后来由Apache.Sun 和其他一些公司及个人共同开发而成,并更名为Tomcat.Tomcat是应用(java)服务器,它是一个servlet容器,是Apache的扩展,但它是独立运行的.tomat应用于Java Servlet, JavaServer Pages,Java Expression Language以及其他的Javaweb开发的技术. Th

取代奶瓶Minidwep-gtk破解WPA 全攻略

取代奶瓶Minidwep-gtk 破 WPA 全攻略  目录 1. CDlinux 下使用 minidwepgtk 获取握手包并使用自带的字典破解 2. 自带的字典破解不出密码时使用 U 盘外挂字典继续暴力破解密码 3. 将握手包拷贝到 Windows 系统下使用 ewsa 工具高速破解密码 4.破解 WPA 加密"握手包"字典的制作 一.CDlinux 下使用 minidwepgtk 获取握手包并使用自带的字典破解 插好网卡,在 minidwep-gtk 上面点鼠标右键选择执行. 跳

Python中文全攻略

Python中文全攻略[转] 这几天一直纠结月python的中文编码问题,发现一篇不错的文章,转过来留个念想. 原文链接:http://blog.csdn.net/mayflowers/archive/2007/04/18/1568852.aspx 1.        在Python中使用中文 在Python中有两种默认的字符串:str和unicode.在Python中一定要注意区分“Unicode字符串”和“unicode对象”的区别.后面所有的“unicode字符串”指的都是python里的