在做移动端web开发时,常遇到click事件点击延迟问题,直接使用click事件就会造成不好的体验,但是我们可以用移动端的touch事件来模拟click事件,于是我花了些时间做了一个tap.js,下面附上代码。
js:
/*!
* tap.js v1.2.2
*/
;(function (factory) {
if (typeof define === 'function' && define.amd) {
define(function(){return factory;});
}else if (typeof exports == "object") {
module.exports = factory;
}else {
window.tap = factory;
}
}(function(){
var arg = arguments,
doc = window.document,
els = arg[0] == doc ? [doc]:doc.querySelectorAll(arg[0]),
isTouch = "ontouchend" in doc,
len = els.length,
i = 0,
isEntrust = typeof arg[1]== 'string',
isMulti = typeof arg[1]== 'object';
if(len == 0){return false;}
while (i < len){
if(isTouch){
var o = {};
els[i].addEventListener('touchstart',function(e){
var t = e.touches[0];
o.startX = t.pageX;
o.startY = t.pageY;
o.sTime = + new Date;
});
els[i].addEventListener('touchend',function(e){
var t = e.changedTouches[0];
o.endX = t.pageX;
o.endY = t.pageY;
if((+ new Date)-o.sTime<300){
if(Math.abs(o.endX-o.startX)+Math.abs(o.endY-o.startY)<20){
handler(e,arg,this);
}
}
o = {};
});
}else{
els[i].addEventListener('click',function(e){
handler(e,arg,this);
});
}
i ++;
}
function handler(e,arg,that){
if(e.target.href){
return window.location = e.target.href;
}
if(isEntrust){
if(equal(e,arg[1])){
prevent(e);
arg[2].call(e.target,e);
}
}else if(isMulti){
for(key in arg[1]){
if(equal(e,key)){
prevent(e);
arg[1][key].call(e.target,e);
break;
}
}
}else{
prevent(e);
arg[1].call(that,e);
}
}
function equal(e,el){
var flag = false;
if(el.indexOf('.') != -1 && e.target.className == el.replace('.','')){
flag = true;
}else if(el.indexOf('#') != -1 && e.target.id == el.replace('#','')){
flag = true;
}else if(e.target.nodeName.toLocaleLowerCase() == el){
flag = true;
}
return flag;
}
/*preventDefault不执行则会引起触发失效bug,
*但不必要则不执行。
*/
function prevent(e){
var tagName = e.target.tagName.toLocaleLowerCase();
if(tagName != 'select' && tagName != 'input' && tagName != 'textarea'){
doc.activeElement.blur();
e.preventDefault();
}
}
}))
demo.html(这里可以直接运行测试)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1.0, user-scalable=0">
<title>tap demo</title>
<style type="text/css">
h4{margin: 10px 0;}
</style>
</head>
<body>
<h3>直接调用</h3>
<div>
<button class="button">tap1</button>
<button class="button">tap2</button>
<button class="button">tap3</button>
<button class="button">tap4</button>
<button class="button">tap5</button>
</div>
<hr>
<h3>事件委托</h3>
<div id="test">
<button class="button2">tap1</button>
<button class="add">add</button>
<button class="doc">document</button>
</div>
<hr>
<h3>事件冒泡</h3>
<h4>正常冒泡</h4>
<div id="parent">
parent<br><br>
<button class="son">son</button>
</div>
<h4>阻止冒泡</h4>
<div id="parent2">
parent2<br><br>
<button class="son2">son2</button>
</div>
<h3>阻止默认动作</h3>
<form action="">
<button class="submit" type="submit">提交</button>
</form>
<h3>代码优化</h3>
<h4>快速跳转</h4>
<div class="a-box">
<a href="https://github.com/weijhfly/tap"><button>里面有子元素</button></a>
<a href="https://github.com/weijhfly/tap">没有子元素</a>
</div>
<br>
<input type="text" placeholder="测试焦点是否正常">
<h4>同时指定多个事件</h4>
<button class="e1">事件1</button>
<button class="e2">事件2</button>
<button class="e3">事件3</button>
<!-- layer.js仅做弹出演示 -->
<script src="https://weijhfly.github.io/js/layer/layer.js"></script>
<script src="https://weijhfly.github.io/js/tap.js"></script>
<script>
//直接调用
tap('.button',function(e){
console.log(e);
var event = "ontouchend" in document? 'tap':'click';
layer.msg(event+' : '+this.innerText);
})
// 事件委托 event delegation
tap('#test','.button2',function(){
var event = "ontouchend" in document? 'tap':'click';
layer.msg(event+' : '+this.innerText);
})
var i = 0;
tap('#test','.add',function(e){
console.log(e);
i ++;
var test = document.getElementById("test");
var button = document.createElement("button");
button.className = 'button2';
button.innerHTML = "button "+i;
test.insertBefore(button,test.childNodes[0]);
})
tap(document,'.doc',function(){
layer.msg('doc');
})
/*
阻止冒泡 stop propagation
在事件委托中仅能阻止委托元素对上层的冒泡
*/
tap('#parent',function(){
setTimeout(function() {
layer.msg('parent');
}, 100);
})
tap('.son',function(){
layer.msg('son');
})
// 阻止冒泡
tap('#parent2',function(){
setTimeout(function() {
layer.msg('parent2');
}, 100);
})
tap('.son2',function(e){
e.stopPropagation();
layer.msg('son2');
})
/*
阻止默认动作
同上,事件委托中仅能阻止委托元素的默认动作
注意:在移动端默认执行e.preventDefault();
*/
tap('.submit',function(e){
e.preventDefault();
e.stopPropagation();
layer.msg('无法提交');
})
/*
* 代码优化
*/
//跳转 **存在href属性
tap(document,'a');
// 通过委托实现多个事件 **默认不能重复,重复只执行第一个
tap(document,{
'.e1':function(){
layer.msg(this.innerText);
},
'.e2':function(){
layer.msg(this.innerText);
},
'.e3':function(){
layer.msg(this.innerText);
}
});
</script>
</body>
</html>
github:https://github.com/weijhfly/tap
有兴趣的可以看下,另外还有vue v-tap指令版的https://github.com/weijhfly/vue-tap,欢迎关注。
此外,在移动端解决点击延迟问题,还是比较推荐fastclick,兼容性较好且方便使用,不过相对而言模拟tap事件体积较小,也可以拿来练手了。