【发布时间】:2010-10-27 04:58:29
【问题描述】:
我正在处理 DOM 元素上的 click 和 dblclick 事件。每个执行不同的命令,但我发现双击元素时,除了触发双击事件外,还触发了两次单击事件。防止这种行为的最佳方法是什么?
【问题讨论】:
标签: javascript dom-events
我正在处理 DOM 元素上的 click 和 dblclick 事件。每个执行不同的命令,但我发现双击元素时,除了触发双击事件外,还触发了两次单击事件。防止这种行为的最佳方法是什么?
【问题讨论】:
标签: javascript dom-events
如果其他人偶然发现这个问题(就像我一样)寻找答案,我能想出的绝对最佳解决方案如下:
$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 */
}
});
完成。如果背靠背点击不止一次,第二次,第三次等。不会开火。我绝对更喜欢使用任何类型的计时器。
通过阅读this,我让自己指向了这个方向。
顺便说一句:我第一次研究这个问题是因为我不小心双击了一个分页链接,事件触发并在回调发生之前完成了两次。
在想出上面的代码之前,我有
if e.originalEvent.detail === 2 //return
但是,我能够点击链接 3 次(三次点击),虽然第二次点击没有触发,但第三次点击了
【讨论】:
$node.on('click', function (e) { if (e.originalEvent.detail > 1 || alreadyClicked == true) { alreadyClicked = false; return; } alreadyClicked = true; //Code here. alreadyClicked = false; } 当然,您必须在脚本主体中声明变量并将其设置在文档中。准备好我知道这个问题很老,但它仍然是一个问题。注意:编辑格式并添加上述句子
在评论中,你说,
我将点击处理程序延迟了 300 毫秒(明显且烦人的延迟),甚至......
所以听起来您想要的是,当您单击时,DOM 应该立即生成一个单击事件,除非单击是双击的第一次单击。
要实现此功能,当您单击时,DOM 需要能够预测这是最终单击还是第一次双击(但我认为一般来说DOM 来预测用户是否要再次点击)。
您尝试在单击和双击时采取的两种不同操作是什么? IMO,在普通应用程序中,您可能需要这两个事件:例如单击以聚焦于一个元素,然后双击以激活它。
当您必须分离事件时,某些应用程序会使用双击以外的方式:例如,它们使用右键单击或 control-click。
【讨论】:
如果您想检测元素被点击了多少次并基于此触发事件,您可以使用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单击。
【讨论】:
AFAIK DOM Level 2 事件没有指定双击。 它在 IE7 上对我不起作用(令人震惊),但 FF 和 Opera 管理规范没有问题,我可以将所有操作附加到点击事件,但双击只需等到“详细信息”属性事件对象的值为 2。来自文档:“如果在同一屏幕位置发生多次点击,则序列重复,详细属性随着每次重复而递增。”
【讨论】:
感谢这里的所有其他答案,因为当交互需要两者但互斥时,它们的组合似乎为我提供了一个合理的解决方案:
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,并附有示例:
【讨论】:
这是我在模块中所做的区分
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 等其他任何东西。
要查看单击和双击之间的适当时间,请参阅this
对不起,我的英语不好。 希望对你有帮助:)
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>
【讨论】:
我知道这已经很老了,但我想无论如何我都会发布,因为我遇到了同样的问题。以下是我的解决方法。
$('#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);
}
}
【讨论】:
您可以使用 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();
}
【讨论】:
const toggle = () => {
watchDouble += 1;
setTimeout(()=>{
if (watchDouble === 2) {
console.log('double' + watchDouble)
} else if (watchDouble === 1) {
console.log("signle" + watchDouble)
}
watchDouble = 0
},200);
}
【讨论】: