移动端点击事件全攻略,这里的坑你知多少?

看标题的时候你可能会想,点击事件有什么好说的,还写一篇攻略?哈哈,若是你这么想,只能说明你too young to simple.javascript

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

这个问题的解决方案就是:zepto.js的tap事件。tap事件能够理解为在移动端的click事件,而zepto.js由于几乎彻底复制jQuery的api,所以经常被用在h5的开发上用来取代jquery.html

因为模块化的缘由,致使有的同窗下载的zepto.js的模块并不全,形成了一大堆悲剧,觉得zepto.js不支持一些方法,因此,下载的时候注意一点。java

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

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

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

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

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

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

因此,在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找到我]的时候,会计算一个滑动距离。

// 实现滑动的大概代码

// 滑动元素translateX的初始值
var iscroll = device_width,

    // 用来计算的中间值
    istarX = 0,
    
    // 手指第一次点在屏幕上的x坐标
    istart_pageX = 0; 
   
// 绑定事件
$area.on('touchstart', touchstart)
     .on('touchmove', touchmove)
     .on('touchend', touchend);

function touchstart(event) {
    event.preventDefault();
    istartX = iscroll;
    istart_pageX = event.originalEvent.changedTouches[0].pageX;
}

function touchmove(event) {

    // 滑动过程当中手指位置x坐标会不停变更,这里会保存一个当前位置与初始位置的一个差值
    var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
    iscroll = istartX + distance;

    // 这里是我自定义的一个css方法,用来设置元素translateX的当前值
    Utils.css(area, { translateX: iscroll });
}

function touchend(event) {
    var distance = event.originalEvent.changedTouches[0].pageX - istart_pageX;
    $area.off('touchstart touchmove touchend');

    // 根据差值的不一样,执行不一样的动做
    if (distance < -80) {
        slideNext(function() {
            addEventSlider($area);
        })
    }
    else if (distance > 80) {
        slidePrev(function() {
            addEventSlider($area);
        })
    }
    else if (distance == 0) {        
        /* 当差值为0时,我认为这是执行了一次点击 */
        
        addEventSlider($area);
    }
    else {
        ani(area).animate(400, 'easeout', { x: -device_width }, function() {
            iscroll = -device_width;
            addEventSlider($area);
        })
    }
}

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

个人这一次尝试,就是在滑动差值distance为0的时候,认为这是一次点击,所以执行点击事件应该有的动做。原本我觉得这就可以解决了,测试的时候也经过了。可是 - -!产品同窗很是有执念的在日历上点击了50多下,结果发现,屡次点击以后,在点就失去效果了!!!

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

http://yangbo5207.github.io/s...

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

使用方式和其余同样。

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

$area.tap(function() {})

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

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

clipboard.png

相关文章
相关标签/搜索