我正在处理 DOM 元素上的 click 和 dblclick 事件。每个执行不同的命令,但我发现双击元素时,除了触发双击事件外,还触发了两次单击事件。防止这种行为的最佳方法是什么?
13 回答
万一其他人偶然发现这个(就像我一样)寻找答案,我能想出的绝对最佳解决方案如下:
$node.on('click',function(e){
if(e.originalEvent.detail > 1){
return;
/* if you are returning a value from this
function then return false or cancel
the event some other way */
}
});
完毕。如果背靠背点击不止一次,第二次,第三次等。不会开火。我绝对更喜欢使用任何类型的计时器。
通过阅读这篇文章,我让自己指向了这个方向。
顺便说一句:我第一次研究这个问题是因为我不小心双击了一个分页链接,事件触发并在回调发生之前完成了两次。
在想出上面的代码之前,我有
if e.originalEvent.detail === 2 //return
但是,我能够点击链接 3 次(三次点击),虽然第二次点击没有触发,但第三次点击了
你在评论中说,
我将点击处理程序延迟了 300 毫秒(一个明显且令人讨厌的延迟),甚至......
因此,听起来您想要的是,当您单击时,DOM 应该立即生成一个单击事件,除非单击是双击的第一次单击。
要实现此功能,当您单击时,DOM 需要能够预测这是最终单击还是第一次双击(但我认为 DOM 通常无法预测用户是否要再次点击)。
您尝试在单击和双击时采取的两种不同的操作是什么?IMO,在一个正常的应用程序中,您可能需要这两个事件:例如单击以关注一个元素,然后双击以激活它。
当您必须分离事件时,某些应用程序会使用双击以外的方式:例如,它们使用右键单击或控制单击。
UIEvent.detail
如果您想检测元素被点击的次数并基于此触发事件,您可以使用。
一个简单的例子:
element.addEventListener("click", function (e) {
if (e.detail === 1) {
// do something if the element was clicked once.
} else if (e.detail === 2) {
// do something else if the element was clicked twice
}
});
在这种情况下,最好稍微延迟单击事件的执行。让您的双击处理程序设置一个单击事件将检查的变量。如果该变量具有特定值,可能是boolDoubleClick == true
,则不要fire/handle
单击。
感谢这里的所有其他答案,因为当交互需要两者但相互排斥时,它们的组合似乎为我提供了一个合理的解决方案:
var pendingClick = 0;
function xorClick(e) {
// kill any pending single clicks
if (pendingClick) {
clearTimeout(pendingClick);
pendingClick = 0;
}
switch (e.detail) {
case 1:
pendingClick = setTimeout(function() {
console.log('single click action here');
}, 500);// should match OS multi-click speed
break;
case 2:
console.log('double click action here');
break;
default:
console.log('higher multi-click actions can be added as needed');
break;
}
}
myElem.addEventListener('click', xorClick, false);
更新:我在这个 Github 存储库中添加了这种方法的通用版本以及用于触摸设备的 click polyfill,并附有示例:
AFAIK DOM Level 2 Events 没有指定双击。它在 IE7 上对我不起作用(令人震惊),但 FF 和 Opera 管理规范没有问题,我可以将所有操作附加到点击事件,但双击只需等到“详细信息”属性事件对象的值为 2。来自文档:“如果在同一屏幕位置发生多次点击,则序列重复,详细属性随着每次重复而递增。”
这是我在模块中所做的区分
node.on('click', function(e) {
//Prepare for double click, continue to clickHandler doesn't come soon enough
console.log("cleared timeout in click",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
_this.clickTimeout = setTimeout(function(){
console.log("handling click");
_this.onClick(e);
},200);
console.log(_this.clickTimeout);
});
node.on('dblclick', function (e) {
console.log("cleared timeout in dblclick",_this.clickTimeout);
clearTimeout(_this.clickTimeout);
// Rest of the handler function
如果我有 dblclick 事件应该做不同的事情,我会在我的项目中使用这个解决方案来防止点击事件动作。
注意:此解决方案仅适用于 click 和 dblclick,而不适用于 Tripleclick 等任何其他内容。
要查看单击和双击之间的适当时间,请参阅此
对不起,我的英语不好。我希望它有帮助:)
var button, isDblclick, timeoutTiming;
var clickTimeout, dblclickTimeout;
//-----
button = $('#button');
isDblclick = false;
/*
the proper time between click and dblclick is not standardized,
and is cutsomizable by user apparently (but this is windows standard I guess!)
*/
timeoutTiming = 500;
//-----
button.on('dblclick', function () {
isDblclick = true;
clearTimeout(dblclickTimeout);
dblclickTimeout = setTimeout(function () {
isDblclick = false;
}, timeoutTiming);
//-----
// here goes your dblclick codes
console.log('double clicked! not click.');
}).on('click', function () {
clearTimeout(clickTimeout);
clickTimeout = setTimeout(function () {
if(!isDblclick) {
// here goes your click codes
console.log('a simple click.');
}
}, timeoutTiming);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" id="button">
click/dblclick on this to see the result
</button>
您可以使用 debounce 将单击处理程序从检测双击/多次单击中释放出来
测试:https ://jsfiddle.net/L3sajybp/
HTML
<div id='toDetect'>
Click or double-click me
</div>
<hr/>
<ol id='info'>
</ol>
JS
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this,
args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
function debounceSingleClickOnly(func, timeout = 500) {
function eventHandler (event) {
const { detail } = event;
if (detail > 1) {
console.log('no double click for you '+ func.name);
console.log('');
return;
}
func.apply(this, arguments);
}
return debounce(eventHandler, timeout);
}
window.toDetect.addEventListener('click', debounceSingleClickOnly(handleSingleClick));
window.toDetect.addEventListener('dblclick', handleDoubleClick);
function handleS() {
console.log('S func');
console.log(this.id);
}
function handleSingleClick(event) {
console.log('single click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('single click'));
window.info.appendChild(divText)
console.group();
console.log('this element was single-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
function handleDoubleClick(event) {
console.log('double click');
const divText = document.createElement('li');
divText.appendChild(document.createTextNode('double click'));
window.info.appendChild(divText);
console.group();
console.log('this element was double-clicked: ' + event.target.id);
console.log(this.id);
console.log('');
console.groupEnd();
}
我知道这已经很老了,但我想无论如何我都会发布,因为我遇到了同样的问题。这是我解决它的方法。
$('#alerts-display, #object-display').on('click', ['.item-data-summary', '.item-marker'], function(e) {
e.preventDefault();
var id;
id = setTimeout(() => {
// code to run here
return false;
}, 150);
timeoutIDForDoubleClick.push(id);
});
$('.panel-items-set-marker-view').on('dblclick', ['.summary', '.marker'], function(e) {
for (let i = 0; i < timeoutIDForDoubleClick.length; i++) {
clearTimeout(timeoutIDForDoubleClick[i]);
}
// code to run on double click
e.preventDefault();
});
这是我防止第二次点击的简单解决方案。当然,我可以在检测到双击时重新启动超时,但实际上我从来不需要它。
clickTimeoutId = null;
onClick(e) {
if (clickTimeoutId !== null) {
// Double click, do nothing
return;
}
// Single click
// TODO smth
clickTimeoutId = setTimeout(() => {
clearTimeout(clickTimeoutId);
clickTimeoutId = null;
}, 300);
}
总而言之,要识别同一元素上的 simpleClick 和 doubleClick 事件,只需使用此方法处理 onClick 事件:
var EVENT_DOUBLE_CLICK_DELAY = 220; // Adjust max delay btw two clicks (ms)
var eventClickPending = 0;
function onClick(e){
if ((e.detail == 2 ) && (eventClickPending!= 0)) {
// console.log('double click action here ' + e.detail);
clearTimeout(eventClickPending);
eventClickPending = 0;
// call your double click method
fncEventDblclick(e);
} else if ((e.detail === 1 ) && (eventClickPending== 0)){
// console.log('sigle click action here 1');
eventClickPending= setTimeout(function() {
// console.log('Executing sigle click');
eventClickPending = 0
// call your single click method
fncEventClick(e);
}, EVENT_DOUBLE_CLICK_DELAY);
// } else { // do nothing
// console.log('more than two clicks action here ' + e.detail);
}
}
const toggle = () => {
watchDouble += 1;
setTimeout(()=>{
if (watchDouble === 2) {
console.log('double' + watchDouble)
} else if (watchDouble === 1) {
console.log("signle" + watchDouble)
}
watchDouble = 0
},200);
}