UDPATE:
我一直在研究一种实现,将 click 和 touchend 事件用于同一个函数,如果类型发生变化,该函数会有效地阻止事件。我的目标是拥有一个响应速度更快的应用程序界面——我想减少事件开始到 UI 反馈循环的时间。
为了使这个实现工作,假设您在“点击”和“触摸结束”上添加了所有相关事件。如果两个事件都需要运行但属于不同类型,这可以防止一个元素被剥夺事件冒泡。
这是一个基于轻量级 API 的实现,我出于演示目的对其进行了简化。它演示了如何在折叠元素上使用该功能。
var tv = {
/**
* @method eventValidator()
* @desc responsible for validating event of the same type.
* @param {Object} e - event object
* @param {Object} element - element event cache
* @param {Function} callback - callback to invoke for events of the same type origin
* @param {Object} [context] - context to pass to callback function
* @param {Array} [args] - arguments array to pass in with context. Requires context to be passed
* @return {Object} - new event cache
*/
eventValidator: function(e, element, callback, context, args){
if(element && element.type && element.type !== e.type){
e.stopPropagation();
e.preventDefault();
return tv.createEventCacheObj({}, true);
} else {
element = tv.createEventCacheObj(e);
(typeof context === "object" ? callback.apply(context, args) : callback());
return element;
}
},
/**
* @method createEventCacheObj()
* @param {Object} event - event object
* @param {String} [event.type] - event type
* @param {Number} [event.timeStamp] - time of event in MS since load
* @param {Boolean} [reset=false] - flag to reset the object
* @returns {{type: *, time: string}}
*/
createEventCacheObj: function (event, reset){
if(typeof reset !== 'boolean') reset = false;
return {
type: !reset ? event.type : null,
time: !reset ? (event.timeStamp).toFixed(2): null
};
}
};
// Here is where the magic happens
var eventCache = [];
var pos = 0;
var $collapses = document.getElementsByClassName('tv-collapse__heading');
Array.prototype.forEach.call($collapses, function(ele){
ele.addEventListener('click', toggleCollapse);
ele.addEventListener('touchend', toggleCollapse);
// Cache mechanism
ele.setAttribute('data-event-cache', String(pos++));
});
/**
* @func toggleCollapse()
* @param {Object} e - event object
* @desc responsible for toggling the state of a collapse element
*/
function toggleCollapse(e){
eventCache[pos] = tv.eventValidator(e, eventCache[pos], function(){
// Any event which isn't blocked will run the callback and its content
// the context and arguments of the anonymous function match the event function context and arguments (assuming they are passed using the last two parameters of tv.eventValidator)
}, this, arguments);
}
原答案:
这是对Rafael Fragoso 的回答的修改 - 纯 JS。
(function(){
button = document.getElementById('sayHi');
button.addEventListener('touchstart', ohHai);
button.addEventListener('click', ohHai);
function ohHai(event){
event.stopPropagation();
event.preventDefault();
console.log('ohHai is:', event.type);
};
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SO - Answer</title>
</head>
<body>
<button id="sayHi">Anyone there?</button>
</body>
</html>
运行以下代码段并注意输出:
- 电话
- 药片
- 平板电脑(桌面模式 - 如果适用)
- 桌面
- 桌面(触摸屏 - 如果适用)
关键是我们正在阻止连续的事件触发。当触摸发生时,移动浏览器会尽力模拟点击。我希望我能找到一篇文章的链接,该链接解释了在 touchstart 之后通过点击发生的所有事件。(我正在寻找双击和单击实际触发之间的 300 毫秒延迟)。
触摸和鼠标设备
我使用 Surface Pro 和带有触摸屏的 Windows 10 桌面进行了几次测试。我发现它们都触发了您所怀疑的事件,touchstart 用于触摸,click 用于触控板、鼠标和造型师。有趣的是,靠近但不在按钮上的触摸事件会在没有触摸事件的情况下触发点击事件。似乎 Windows 10 中的内置功能会查找半径内最近的节点,如果找到节点,它将触发基于鼠标的事件。
同一类型的多个事件
如果一个元素上有两个相同类型的事件,阻止事件冒泡可能会阻止其中一个事件触发。有几种不同的方法可以使用某种缓存来处理这个问题。我最初的想法是修改事件对象,但我们得到了一个参考,所以我认为缓存解决方案就足够了。