在移动设备上浏览网页时,用户体验至关重要。最近,在开发一个新项目时,遇到了一个影响用户体验的问题:在移动设备上,点击事件的触发时间较长,这显著降低了应用的可用性。例如,双击操作会被识别为单次点击,导致第二次点击丢失。这种情况发生的原因是移动设备支持基于触摸的事件架构。
在HTML中,可以指定ontouchstart
、ontouchend
和ontouchmove
等新事件,这些事件在移动设备上可以立即触发。延迟的原因是手机浏览器会在触发点击事件之前,一直寻找触摸事件,直到window对象。
为了解决这个问题,探索了多种方法。一个显而易见的方法是同时指定触摸事件和点击事件。但不想重复,尤其是因为不需要触摸事件带来的多触摸和手势等好处。还考虑了使用JavaScript在页面加载时遍历DOM,并重新分配所有点击事件为触摸事件。这种方法在不修改DOM的情况下是有效的,但如果在重新分配后修改了DOM而没有再次重新分配,就会造成问题。
最终,编写了以下脚本,它跨浏览器兼容,并且假设只是想让一个点击编程的网站在手机上工作得更好,那么它就像梦想一样工作!
以下是代码块,请查看注释以了解其工作原理。它适用于IE8+、Chrome、Safari Win & Mac、Firefox。
// 此方法返回true,如果浏览器和设备支持触摸事件
function touchDeviceTest() {
try {
document.createEvent("TouchEvent");
return true;
} catch(e) {
return false;
}
}
// 一个标志,用于指示触摸是否已滚动
var tchHasScrolled = false;
// 为此触摸事件生成的随机数
var tchRandomId = 0.0;
// 为上一个触摸事件生成的随机数
var tchRandomLastId = 0.0;
// 当前触摸的临时y位置
var touchYTemp = 0;
// 这是文档滚动事件方法
function onDocumentScroll(e) {
tchHasScrolled = true;
}
// 这是文档点击事件方法
function onDocumentClick(e) {
// 如果触摸按下和触摸抬起的随机数不同
if (tchRandomId != tchRandomLastId) {
// 设置上一个为当前
tchRandomLastId = tchRandomId;
// 如果是触摸设备
if (touchDeviceTest()) {
// 如果目标不是锚点
if (e.target.tagName != 'A') {
// 停止正常行为
e.stopPropagation();
e.preventDefault();
}
}
}
}
// 这是文档触摸开始方法
function onDocumentTouchStart(e) {
// 设置已滚动为false
tchHasScrolled = false;
// 分配一个新的随机id
tchRandomId = new Date().getMilliseconds() + Math.random();
// 存储触摸的y点
touchYTemp = e.touches[0].clientY;
}
// 这是文档触摸移动方法
function onDocumentTouchMove(e) {
// 如果触摸移动超过10px,将滚动标志设置为true
var y = e.touches[0].clientY;
if (y > touchYTemp + 10 || y < touchYTemp - 10) {
tchHasScrolled = true;
}
}
// 这是文档触摸结束方法
function onDocumentTouchEnd(e) {
// 如果仍然有触摸,管理滚动
if (e.touches[0]) {
var y = e.touches[0].clientY;
if (y > touchYTemp + 10 || y < touchYTemp - 10) {
tchHasScrolled = true;
}
}
// 如果文档没有滚动
if (!tchHasScrolled) {
// 获取事件目标
var target = e.target.parentNode;
// 如果目标有点击事件
if (e.target.onclick) {
// 调用点击事件
e.target.onclick(e);
} else {
// 否则,循环遍历父级寻找点击事件
while (target != null) {
if (target.onclick) {
// 调用点击事件
target.onclick(e);
break;
}
target = target.parentNode;
}
}
}
}
// 将方法分配给事件
if (!document.addEventListener) {
document.attachEvent('onclick', onDocumentClick);
document.attachEvent('onscroll', onDocumentScroll);
document.attachEvent('ontouchstart', onDocumentTouchStart);
document.attachEvent('ontouchmove', onDocumentTouchMove);
document.attachEvent('ontouchend', onDocumentTouchEnd);
} else {
document.addEventListener('click', onDocumentClick, true);
document.addEventListener('scroll', onDocumentScroll, true);
document.addEventListener('touchstart', onDocumentTouchStart, true);
document.addEventListener('touchmove', onDocumentTouchMove, true);
document.addEventListener('touchend', onDocumentTouchEnd, true);
}