UDPATE:
我一直致力于为同一个函数同时使用点击和触摸事件的实现,如果类型发生变化,该函数会有效地阻止事件。我的目标是有一个反应更灵敏的应用程序界面——我想减少从事件开始到UI反馈循环的时间。
为了让这个实现工作,假设你已经在'click'和'touchend'上添加了所有相关事件。这可以防止在需要运行两个不同类型的事件时剥夺一个元素的事件冒泡。
下面是一个基于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);
}
最初的回答:
这里有一个回应,这是拉斐尔Fragoso的答案的修改-纯JS。
(函数(){
button = document.getElementById('sayHi');
按钮。addEventListener (touchstart, ohHai);
按钮。addEventListener(“点击”,ohHai);
函数ohHai(事件){
event.stopPropagation ();
event.preventDefault ();
console.log('ohHai is:', event.type);
};
}) ();
<!DOCTYPE html >
< html lang =“en”>
< >头
< meta charset = " utf - 8 " >
<title>SO - Answer</title>
> < /头
<身体>
<button id="sayHi">有人吗?< / >按钮
< /身体>
< / html >
运行下面的代码片段,并注意输出:
电话
平板电脑
平板电脑(桌面模式-如适用)
桌面
桌面(触摸屏-如适用)
关键是我们要阻止连续事件的爆发。移动浏览器尽最大努力在触摸发生时模拟点击。我希望我能找到一篇文章的链接,这篇文章解释了触摸启动后通过点击发生的所有事件。(我正在搜索双击和点击实际发射之间的300ms延迟)。
触摸和鼠标设备
I ran a couple of tests using a Surface Pro and a windows 10 desktop with a touchscreen. What I found was that they both triggered events as you would suspect, touchstart for touches and click for trackpad, mouse, and stylist. The interesting thing was that a touch event which was near, but not on the button, would triggering a click event without a touch event. It seems that the built in functionality in Windows 10 looks for the closest nodes within a radius and if a node is found it will fire a mouse based event.
同一类型的多个事件
如果一个元素上有两个相同类型的事件,停止该事件冒泡可以防止其中一个事件触发。有几种不同的方法可以使用某种缓存来处理这个问题。我最初的想法是修改事件对象,但我们得到了一个引用,所以我认为缓存解决方案将必须足够。