当双击事件发生时,如何防止单击事件触发

57

我正在处理DOM元素上的单击和双击事件。每个事件执行不同的命令,但是当双击该元素时,除了触发双击事件外,还会触发两次单击事件。有什么防止这种行为的最佳方法吗?

14个回答

39

如果有其他人像我一样偶然发现这个问题并在寻找答案,我能想到的最好解决方案如下:

    $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

然而,我成功地点击了链接三次(三连击),尽管第二次点击没有生效,但第三次点击生效了。


4
这将防止第二次点击触发,仍然允许第一次点击触发。如果我理解正确,OP想要在有第二次点击时禁用第一次点击。我认为最好的方法是使用一个小延迟(我使用200毫秒,但希望能找到更好的解决方案)。 - ClarkeyBoy
5
不适用于IE 11 x64(Win8.1):event.detail始终为0,无论是单击还是双击。 - robert4
1
@robert4 由于12%的用户仍在使用IE,这太糟糕了。但是,以下代码似乎可以解决问题:$node.on('click', function (e) { if (e.originalEvent.detail > 1 || alreadyClicked == true) { alreadyClicked = false; return; } alreadyClicked = true; //Code here. alreadyClicked = false; })当然,您需要在脚本主体中声明变量并在document.ready中设置它。 我知道这个问题很旧,但它仍然存在问题。 - NotImplementedException
5
但是,正如@ClarkeyBoy所说,使用这种方法,双击事件会首先触发一个detail为1的单击事件,然后再触发一个detail为2的第二个事件。请参见jsfiddle。因此据我所知,这并没有回答OP的问题。 - Fabien Snauwaert
1
e.originalEvent 未定义。 - Rahul
显示剩余7条评论

20
在你的评论中,你说:
所以听起来你想要的是当你单击时DOM应该立即生成一个点击事件,但如果这个点击是双击的第一次点击,则不会执行。
为了实现这个功能,当你单击时,DOM需要能够预测这是否是最后一次点击,或者它是双击的第一次点击(然而我认为DOM通常无法预测用户是否即将再次点击)。
两个你尝试在单击和双击上执行的不同操作是什么?在正常应用程序中,您可能希望同时使用单击和双击事件:例如单击聚焦于元素,然后双击激活它。
当你必须分开这些事件时,一些应用程序使用除了双击之外的其他东西:例如右键单击或控制单击。

1
谢谢你更新的答案。我觉得问题的关键在于我需要重新思考我的用户界面。我想要实现的目标其实是不可能的。我应该只使用两个控件来完成这两个动作,或者使用修饰键而不是双击。谢谢! - David

16
您可以使用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
  }
});

这可能是有效的,但要小心,因为它可能会变得过时。 - remborg
@remborg,截至2022年7月,它的状态如何? - user13944038
@remborg 它仍然受支持 https://developer.mozilla.org/zh-CN/docs/Web/API/UIEvent/detail - tarek salem

6

感谢这里所有其他答案的贡献,它们的结合似乎为我提供了一个合理的解决方案,当交互需要同时使用,但又互相排斥时:

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仓库中添加了一个通用版本的方法,并为触摸设备添加了一个点击填充示例:

https://github.com/mckamey/doubleTap.js


6
在这种情况下,最好稍微延迟单击事件的执行。让您的双击处理程序设置一个变量,以供单击事件检查。如果该变量具有特定值,可以是 boolDoubleClick == true,那么不要 fire/handle 单击事件。

实际上,这就是我现在正在做的事情,但似乎这是一个相当笨拙的解决方案。我将点击处理程序延迟了300毫秒(明显且令人烦恼的延迟),即使如此,您仍然可以双击得足够慢(例如350毫秒)以使它们都触发。 - David
有趣。可以合理地假设,如果他们在第二次点击上拖得太久,那么期望的效果将是2个单击。 - Jordan S. Jones
很不幸,双击时间是由最终用户进行配置的(例如使用控制面板/鼠标),我想浏览器中的JavaScript无法获取/确定配置为多长时间延迟。 - ChrisW
300毫秒似乎太长了,我会尝试缩短一半。 - Ringo
通常你可以在操作系统设置中更改双击延迟。例如,在 macOS 上,您可以将此值设置为非常长的时间。这打破了您硬编码的任何合理延迟。 - Jespertheend

3

这是我在模块内区分的方法:

       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

3
据我所知,DOM Level 2 Events没有关于双击的规定。在IE7上无法使用(这让人感到震惊),但是FF和Opera没有问题,可以将所有操作附加到单击事件上,但是对于双击,只需等待事件对象的“detail”属性为2即可。从文档中可以看到:“如果多次点击发生在相同的屏幕位置,则序列将重复,并且每次重复时detail属性都会增加。”

这实际上是一个非常优雅的解决方案。它仍然需要一个setTimout/clearTimeout组合来处理第一次点击,但它简化了两者之间的协调,甚至为三次点击等操作打开了大门。谢谢! - mckamey

2

我在我的项目中使用这个解决方案来防止点击事件的触发,如果我有一个双击事件需要执行不同的操作。

注意:这个解决方案仅适用于单击和双击事件,而不包括三击等其他事件。

要查看单击和双击之间的正确时间间隔,请参见此处

抱歉我的英语不太好,希望这对你有所帮助 :)

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>


需要额外的7,265行JS来执行这个功能似乎有些过度。 - Reggie Pinkham
@ReggiePinkham 我知道,请使用 addEventListener,除非您不使用 jQuery 的其他功能。 - MMDM

2
这可以通过以下代码实现。

var clickHandler = function(e) { /* put click event handling code here */ };
var doubleclickHandler = function(e) { /* put doubleclick event handling code here */ }

const maxMsBetweenClicks = 300;
var clickTimeoutId = null;
document.addEventListener("dblclick", handleDoubleClick);
document.addEventListener("click",    handleSingleClick);

function handleSingleClick(e){ 
    clearTimeout(clickTimeoutId);  
    clickTimeoutId = setTimeout( function() { clickHandler(e);}, maxMsBetweenClicks);
}
    
function handleDoubleClick(e){ 
    clearTimeout(clickTimeoutId); 
    doubleclickHandler(e); 
}

0

我知道这个问题很老了,但是我还是想发一篇文章,因为我最近遇到了同样的问题。以下是我解决它的方法。

 $('#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();
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接