使用原生JS封装Tap事件,解决移动端300ms延迟

为了防止误操作,移动端iOS操作系统针对原生click事件做了300ms的延迟,这在一定程度上影响了我们的使用体验。

GitHub项目地址:https://github.com/SimonZhangITer/MyTapEvent

FastClick

现在有现成的插件fastclick可以解决这个问题,但是也有弊端:

  • GitHub上最新版本的插件大小为25.4kb,轻量为趋势,能省则省。
  • 它的核心思想是取消默认的click时间,判断当前dom节点的类型进行相应的操作,这个判断过程较为繁琐。

MyTapEvent

本人最近在做微信项目,由于fastclick插件存在一定弊端,因此开发了一个简单的tap事件,主要思想有以下几点:

Thinking

  • 一次tap事件包含touchstart和touchmove(轻微移动)以及touchend三种状态
  • callback方法在touchend后执行
  • 根据chrome浏览器默认的判断取消点击的移动量,手指偏移量(水平或垂直)超过15px则判定为滚动,取消执行tap事件
  • 手指按下时间过长不视为点击,默认时间间隔为500ms
  • 使用HTMLElement来扩充原型,方便添加Event
  • 使用单例模式,确保只加载一次

ok,思想定下来,代码写起来就清晰多了:

if (!HTMLElement.prototype.addTapEvent) {
    HTMLElement.prototype.addTapEvent = function(callback) {
        var tapStartTime = 0,
            tapEndTime = 0,
            tapTime = 500, //tap等待时间,在此事件下松开可触发方法
            tapStartClientX = 0,
            tapStartClientY = 0,
            tapEndClientX = 0,
            tapEndClientY = 0,
            tapScollHeight = 15, //水平或垂直方向移动超过15px测判定为取消(根据chrome浏览器默认的判断取消点击的移动量)
            cancleClick = false;
        this.addEventListener(‘touchstart‘, function() {
            tapStartTime = event.timeStamp;
            var touch = event.changedTouches[0];
            tapStartClientX = touch.clientX;
            tapStartClientY = touch.clientY;
            cancleClick = false;
        })
        this.addEventListener(‘touchmove‘, function() {
            var touch = event.changedTouches[0];
            tapEndClientX = touch.clientX;
            tapEndClientY = touch.clientY;
            if ((Math.abs(tapEndClientX - tapStartClientX) > tapScollHeight) || (Math.abs(tapEndClientY - tapStartClientY) > tapScollHeight)) {
                cancleClick = true;
            }
        })
        this.addEventListener(‘touchend‘, function() {
            tapEndTime = event.timeStamp;
            if (!cancleClick && (tapEndTime - tapStartTime) <= tapTime) {
                callback();
            }
        })
    }
}

Usage

HTMLElement.addTapEvent(function(){
    //do something...
})
如:
document.querySelect(‘#test‘).addTapEvent(function(){
    alert(‘this is a tap event‘);
})

Case

这里给一个移动端案例,同时也包含了闭包的知识,前20项为tap事件,后30项为click事件,大家可以在手机上试一下效果,感受一下两种方法的差别:

<style media="screen">
    li {
        padding: 20px;
    }

</style>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-touch-fullscreen" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta name="format-detection" content="telephone=no,email=no">
        <meta name="x5-orientation" content="portrait">
        <title>test</title>
    </head>
    <body>
        <ul></ul>
    </body>
    <script type="text/javascript">
        if (!HTMLElement.prototype.addTapEvent) {
            HTMLElement.prototype.addTapEvent = function(callback) {
                var tapStartTime = 0,
                    tapEndTime = 0,
                    tapTime = 500, //tap等待时间,在此事件下松开可触发方法
                    tapStartClientX = 0,
                    tapStartClientY = 0,
                    tapEndClientX = 0,
                    tapEndClientY = 0,
                    tapScollHeight = 15, //水平或垂直方向移动超过15px测判定为取消(根据chrome浏览器默认的判断取消点击的移动量)
                    cancleClick = false;
                this.addEventListener(‘touchstart‘, function() {
                    tapStartTime = event.timeStamp;
                    var touch = event.changedTouches[0];
                    tapStartClientX = touch.clientX;
                    tapStartClientY = touch.clientY;
                    cancleClick = false;
                })
                this.addEventListener(‘touchmove‘, function() {
                    var touch = event.changedTouches[0];
                    tapEndClientX = touch.clientX;
                    tapEndClientY = touch.clientY;
                    if ((Math.abs(tapEndClientX - tapStartClientX) > tapScollHeight) || (Math.abs(tapEndClientY - tapStartClientY) > tapScollHeight)) {
                        cancleClick = true;
                    }
                })
                this.addEventListener(‘touchend‘, function() {
                    tapEndTime = event.timeStamp;
                    if (!cancleClick && (tapEndTime - tapStartTime) <= tapTime) {
                        callback();
                    }
                })
            }
        }

        var ul = document.querySelector(‘ul‘);
        for (var i = 1; i <= 20; i++) {
            var li = document.createElement(‘li‘);
            li.innerHTML = i;
            li.addTapEvent(function() {
                var x = i;
                return function() {
                    alert(x);
                }
            }())
            ul.appendChild(li);
        }
        for (var j = 21; j <= 50; j++) {
            var li = document.createElement(‘li‘);
            li.innerHTML = j;
            li.onclick = function() {
                var x = j;
                returnfunction() {
                    alert(x);
                }
            }()
            ul.appendChild(li);
        }
    </script>
</html>

? 著作权归作者所有

时间: 2024-11-06 03:48:12

使用原生JS封装Tap事件,解决移动端300ms延迟的相关文章

原生js封装tap

// tap事件封装function tap(obj, callBack){ if(typeof obj != 'object') return; // 变量 var startTime = 0; // 记录触摸开始时间 var isMove = false; // 记录是否产生移动 obj.addEventListener('touchstart',function(){ startTime = Date.now(); }); obj.addEventListener('touchmove',

原生JS封装AJAX

今天我们来说说利用原生JS封装AJAX. jquery框架的AJAX方法确实很好用,但有时候我们写的页面需要引入多个JS插件,不一定哪个插件就会和jquery发生冲突,导致jquery用不了了.或者页面比较简单,不需要加重浏览器的负担,这时我们自己封装一个AJAX就是一个很好的办法. //将数据转换成 a=1&b=2格式;function json2url(json){   var arr = [];   //加随机数防止缓存;   json.t = Math.random();   for(v

原生Js封装的弹出框

<!DOCTYPE HTML> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>原生Js封装的弹出框-弹出窗口-页面居中-多状态可选</title> <style type="text/css"&

用jQuery基于原生js封装的轮播

我发现轮播在很多网站里面都用到过,一个绚丽的轮播可以为网页增色不少,最近闲来无事,也用原生js封装了一个轮播,可能不像网上的插件那么炫,但是也有用心去做.主要用了闭包的思想.需要传递的参数有:图片地址的数组,图片宽度,上一页,下一页的id,图片列表即ul的id(这儿使用无序列表排列的图片),自动轮播间隔的时间.功能:实现了轮播的自动轮播,可以点击上一页,下一页进行切换. 下面是html中的代码,只需要把存放的容器写好,引入jquery即可: <!DOCTYPE html><html>

原生JS封装Ajax插件(同域&amp;&amp;jsonp跨域)

抛出一个问题,其实所谓的熟悉原生JS,怎样的程度才是熟悉呢? 最近都在做原生JS熟悉的练习... 用原生Js封装了一个Ajax插件,引入一般的项目,传传数据,感觉还是可行的...简单说说思路,如有不正确的地方,还望指正^_^ 一.Ajax核心,创建XHR对象 Ajax技术的核心是XMLHttpRequest对象(简称XHR),IE5是第一款引入XHR对象的浏览器,而IE5中的XHR对象是通过MSXML库中的一个ActiveX对象实现的,因此在IE中可能有3个版本,即MSXML2.XMLHttp.

原生js跨浏览事件绑定(移除)/javascript事件绑定(移除)

网上找了半天没看到,都是基于ie attachEvent(有诸多的问题存在)来写的,所以手动写了个js模拟绑定事件希望能帮到需要的童鞋 addEvent = function(obj,type,fn){  if(obj.addEventListener) {//W3C   obj.addEventListener(type,fn,false);  }else if (obj.attachEvent) {//ie(期待它的灭亡)   if(!obj.events) obj.events = {};

用原生js封装轮播图

原生js封装轮播图 对于初学js的同学来说,轮播图还是一个难点,尤其是原生js封装轮播图代码,下面是我之前做的一个轮播图项目中封装好的一些代码,有需要的同学可以看一下,有什么不懂的可以看注释,注释看不懂的可以直接私信我 slide.js /* * 轮播图 */ function Slide(elem, ms, d, ind){ this.el = elem; this.w = parseInt(getStyle(elem, "width")); this.h = parseInt(ge

原生JS封装创建多级菜单函数

手写一个使用原生JS封装的多级菜单的函数,满足以下几点需求. 子类层级不确定,可根据数据自动生成多级菜单. 操作便捷,只需传入一个HTML标签. 缺点: 需要满足特定的数据结构 废话不多说,展示代码.先展示数据代码,注意,使用此封装函数需要满足此数据结构. var data = [{ id: 1, address: "安徽", parent_id: 0 }, { id: 2, address: "江苏", parent_id: 0 }, { id: 3, addre

原生js自动触发事件

熟悉jquery的童鞋都知道在jq中有一个方法可以自动触发事件,那就是trigger(),那么通过原生js又怎么模拟触发呢? js中添加一个主动触发事件的方法有dispatch.该方法能模拟用户行为,如点击(click)操作等. 标准使用dispatchEvent方法,IE6/7/8则使用fireEvent方法. dispatchEvent() 方法给节点分派一个合成事件. 语法如下: dispatchEvent(eventObj) eventObj 参数是一个描述事件的 ActionScrip