如何区分单击事件和双击事件?(涉及IT技术)

164

我有一个带有id为"my_id"的li元素中只有一个按钮。我给这个元素附加了两个jQuery事件。

1.

$("#my_id").click(function() { 
    alert('single click');
});

2.

$("#my_id").dblclick(function() {
    alert('double click');
});

但每次它都给我单击

单击始终需要触发,除非在指定的双击时间内实际发生双击,否则无法知道是否会发生双击。 - Pieter Germishuys
8
这是可能的,检查我的答案。 你必须在第一次点击后等待几毫秒,并检查在指定时间内是否有新的点击。 通过这种方式,你就能知道这是一个简单点击还是双击。 - Adrien Schuler
生活中的一个缺点是,如果你想区分并利用这两个事件,那么你可能需要延迟单击操作,直到你知道是否有双击事件正在发生。如果你有理由不触发两个事件。 - Gunnar Forsgren - Mobimation
这里有一些计时器使用示例。https://stackoverflow.com/questions/61461518/javascript-how-prevent-dblclick-double-click-to-also-fire-a-single-click-even - Gunnar Forsgren - Mobimation
我发现以下3个答案对于理解所有概念非常有帮助:1. 事件顺序,2. event.detail 区分单击和双击,3. 使用定时器的现代解决方案 - Gangula
22个回答

109

原来我们使用更多的临时状态和setTimeout,但事实证明,有一个本地属性称为detail可以从event对象中访问!

element.onclick = event => {
   if (event.detail === 1) {
     // it was a single click
   } else if (event.detail === 2) {
     // it was a double click
   }
};

现代浏览器甚至包括IE-9都支持它 :)

来源:https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail


最佳答案 :) - sk8terboi87 ツ
24
在双击情况下,仍然会触发单击事件(其中 event.detail === 1)。请参阅我的 fiddle此线程上的评论 - Fabien Snauwaert
好观点 @FabienSnauwaert。我想我们不能在特定元素上绑定单击和双击事件处理程序; 上述方法有助于明确确定双击,而不需使用计时器等东西。 - kyw
2
你仍然需要在第一次点击时设置一个计时器,以避免触发单击事件。请查看我下面的答案https://dev59.com/w2035IYBdhLWcg3wT-NC#60177326 - user670839
12
提示:如果键盘触发点击事件(例如,当用户使用Tab键聚焦按钮并按Enter时),则事件将生成detail属性为0。因此,像 if(evt.detail !== 1) return; 这样的操作拒绝意外的双击将阻止没有鼠标的用户访问该按钮。 - gristow
evt.detail 还会追踪双击之外的其他点击,如三连击。 - CubicleSoft

89

dblclick事件的行为在Quirksmode中有解释。

dblclick的事件顺序如下:

  1. mousedown
  2. mouseup
  3. click
  4. mousedown
  5. mouseup
  6. click
  7. dblclick

唯一的例外是(当然)Internet Explorer,它们使用自定义顺序:

  1. mousedown
  2. mouseup
  3. click
  4. mouseup
  5. dblclick

如你所见,同时在相同元素上监听这两个事件将导致额外调用click处理程序。


1
不确定 dblclick 是否可靠... 如果我单击一次... 等待一两秒钟,然后再次单击,第二次单击会触发 dblclick 事件! - Michael
这样的长按对于重命名文件等操作非常有用。如果有像“longClick”这样的参数来区分它,那就太好了。 - PeterM
2
event.detail 可用时,它是从点击处理程序检测双击的最佳方法。请参见此答案 - Carl G
被踩的原因不是回答错误或糟糕,而是因为它已经过时了,因为IE基本上不再使用,市场份额只有1%。 - Waruyama

79

在第一次点击后,您需要使用超时来检查是否有另一个点击事件。

这里是技巧

// Author:  Jacek Becela
// Source:  http://gist.github.com/399624
// License: MIT

jQuery.fn.single_double_click = function(single_click_callback, double_click_callback, timeout) {
  return this.each(function(){
    var clicks = 0, self = this;
    jQuery(this).click(function(event){
      clicks++;
      if (clicks == 1) {
        setTimeout(function(){
          if(clicks == 1) {
            single_click_callback.call(self, event);
          } else {
            double_click_callback.call(self, event);
          }
          clicks = 0;
        }, timeout || 300);
      }
    });
  });
}

用法:

$("button").single_double_click(function () {
  alert("Try double-clicking me!")
}, function () {
  alert("Double click detected, I'm hiding")
  $(this).hide()
})
<button>Click Me!</button>

编辑:

如下所述,建议使用本地的 dblclick 事件:http://www.quirksmode.org/dom/events/click.html

或者使用jQuery提供的事件:http://api.jquery.com/dblclick/


20
这在一般情况下似乎效果不错,但是允许两次点击被解释为双击的时间差是否不同取决于系统配置呢?更安全的方法可能是依赖于dblclick事件。 - Jon z
1
是的,我认为你是对的,“dblclick”绝对是今天最好的选择。jQuery也处理此事件:http://api.jquery.com/dblclick/ - Adrien Schuler
15
根据您提供的文档,建议不要同时为同一个元素绑定单击(click)和双击(dblclick)事件处理程序。问题是关于同时拥有这两种事件的情况。 - Álvaro González
1
event.detail可用时,它是检测双击最好的方法。请参见此答案 - Carl G
很好的解决方案Adrien,我需要它用于一个对象上的单击和双击事件,只有一个应该被触发。因此,您的解决方案对我来说非常完美。我只是稍微改进了一下,以便更好地控制“this”:https://playcode.io/492022 - BernhardS

41
现代正确的答案是接受的答案和@kyw的解决方案的混合。 您需要一个超时来防止第一次单击,使用 event.detail 检查以防止第二次单击。

const button = document.getElementById('button')
let timer
button.addEventListener('click', event => {
  if (event.detail === 1) {
    timer = setTimeout(() => {
      console.log('click')
    }, 200)
  }
})
button.addEventListener('dblclick', event => {
  clearTimeout(timer)
  console.log('dblclick')
})
<button id="button">Click me</button>


解决了我的问题。唯一的问题是它稍微影响了我的性能,但那非常微不足道。 - Aniket Jadhav

27
一个简单的函数。不需要jquery或其他框架。将您的函数作为参数传递即可。

一个简单的函数。不需要使用jQuery或其他框架。将您的函数作为参数传递即可。

<div onclick="doubleclick(this, function(){alert('single')}, function(){alert('double')})">click me</div>
    <script>
        function doubleclick(el, onsingle, ondouble) {
            if (el.getAttribute("data-dblclick") == null) {
                el.setAttribute("data-dblclick", 1);
                setTimeout(function () {
                    if (el.getAttribute("data-dblclick") == 1) {
                        onsingle();
                    }
                    el.removeAttribute("data-dblclick");
                }, 300);
            } else {
                el.removeAttribute("data-dblclick");
                ondouble();
            }
        }
    </script>

2
我认为这应该是最好的答案。然而,我不得不将超时时间减少到200才能在Windows IE8上运行,但这个值可能与操作系统和用户有关。并且保存了计时器引用以便在注册双击时取消单击的执行。 - xpereta
感谢您提供的这段代码,它在Chrome和Firefox浏览器上运行得非常好(尽管Chrome不需要这个hack来处理双引号和单引号)。 - victor
现在使用 el.dataset.dblclick 是更好的方法。 - WORMSS
event.detail 可用时,它是从点击处理程序检测双击的最佳方法。请参见此答案 - Carl G

12

恐怕这种行为取决于浏览器:

不建议为同一元素绑定单击和双击事件处理程序。 触发的事件顺序因浏览器而异,有些浏览器在双击之前接收两个单击事件,而其他浏览器只接收一个单击事件。 双击灵敏度(被检测为双击的点击之间的最大时间)可能因操作系统和浏览器而异,通常可由用户进行配置。

http://api.jquery.com/dblclick/

在Firefox中运行您的代码,click()处理程序中的alert()会防止您再次点击。 如果删除此警告,则可以同时获得两个事件。


1
event.detail 可用时,它是从点击处理程序检测双击的最佳方法。请参见此答案 - Carl G

11

要双击(即点击两次),您必须先单击一次。 click() 处理程序在第一次单击时触发,由于警报弹出,您没有机会进行第二次单击以触发 dblclick() 处理程序。

将处理程序更改为执行与 alert() 不同的其他操作,您将看到不同的行为。 (例如更改元素的背景颜色):

$("#my_id").click(function() { 
    $(this).css('backgroundColor', 'red')
});

$("#my_id").dblclick(function() {
    $(this).css('backgroundColor', 'green')
});

8

此答案随时间的推移而过时,请查看@kyw的解决方案。

我创建了一个灵感来自@AdrienSchuler发布的要点的解决方案。仅当您想将单击和双击绑定到元素时才使用此解决方案。否则,我建议使用本机clickdblclick侦听器。

这些是不同之处:

  • Vanillajs,无依赖项
  • 不等待setTimeout处理单击或双击处理程序
  • 双击时,首先触发单击处理程序,然后触发双击处理程序

Javascript:

function makeDoubleClick(doubleClickCallback, singleClickCallback) {
    var clicks = 0, timeout;
    return function() {
        clicks++;
        if (clicks == 1) {
            singleClickCallback && singleClickCallback.apply(this, arguments);
            timeout = setTimeout(function() { clicks = 0; }, 400);
        } else {
            timeout && clearTimeout(timeout);
            doubleClickCallback && doubleClickCallback.apply(this, arguments);
            clicks = 0;
        }
    };
}

使用方法:

var singleClick = function(){ console.log('single click') };
var doubleClick = function(){ console.log('double click') };
element.addEventListener('click', makeDoubleClick(doubleClick, singleClick));

以下是在jsfiddle中的使用方法,jQuery按钮是被接受答案的行为。

jsfiddle


2
上面的代码和你的“Vanilla”版本都不能工作...这里有一个修复后的Vanilla版本:http://jsfiddle.net/jeum/cpbwx5vr/19/。 - jeum
@jeum 在每个浏览器中它仍然能正常运行。哪里出现了“不工作”的问题?你期望的是什么?看起来你使用的版本没有按照我的意图实现。 - A1rPun
首先,我已经修改了我的版本(删除了双重闭包),请尝试新的链接:http://jsfiddle.net/jeum/cpbwx5vr/20/。你的 fiddle(Vanilla 版本)存在问题,即双击会触发单个处理程序:对我来说,它会检索单个 + 双击。 - jeum
@jeum 那个会出错。是的,我的答案与此处接受的和其他答案不同,并且我在这一行中也指明了这种差异:先单击,然后再双击(不要直接双击) - A1rPun
显示剩余3条评论

7

这是另一个基于A1rPun答案的简单的Vanilla解决方案(请参见他的fiddle获取jQuery解决方案,两者都在这里)。

似乎为了避免用户双击时触发单击处理程序,必须在延迟后触发单击处理程序...

var single = function(e){console.log('single')},
    double = function(e){console.log('double')};

var makeDoubleClick = function(e) {

  var clicks = 0,
      timeout;

  return function (e) {

    clicks++;

    if (clicks == 1) {
      timeout = setTimeout(function () {
        single(e);
        clicks = 0;
      }, 250);
    } else {
      clearTimeout(timeout);
      double(e);
      clicks = 0;
    }
  };
}
document.getElementById('btnVanilla').addEventListener('click', makeDoubleClick(), false);

3
如果你不想触发单击事件,这确实是最好的选择。 - A1rPun
event.detail可用时,它是从单击处理程序检测双击的最佳方法。请参见此答案 - Carl G

7

如何区分单击和双击同一元素?

如果您不需要混合使用它们,可以依靠clickdblclick,每个都可以很好地完成工作。

当尝试混合使用它们时会出现问题:dblclick事件实际上也会触发click事件,因此您需要确定单击是“独立”的单击还是双击的一部分。

此外:不应在同一元素上同时使用clickdblclick

不建议为同一元素绑定单击和双击事件处理程序。触发的事件序列因浏览器而异,有些浏览器在双击之前接收到两个单击事件,而其他浏览器只有一个单击事件。双击灵敏度(检测为双击的点击之间的最大时间)可能因操作系统和浏览器而异,并且通常可以由用户进行配置。
来源:https://api.jquery.com/dblclick/

现在好消息来了:

您可以使用事件的detail属性来检测与事件相关的点击次数。这使得在click内部检测双击相当容易。

问题仍然存在于检测单击以及它们是否是双击的一部分。为此,我们需要使用定时器和setTimeout

将所有内容绑定在一起,使用数据属性(避免全局变量)而无需自己计算点击次数:

HTML:

<div class="clickit" style="font-size: 200%; margin: 2em; padding: 0.25em; background: orange;">Double click me</div>

<div id="log" style="background: #efefef;"></div>

JavaScript:

<script>
var clickTimeoutID;
$( document ).ready(function() {

    $( '.clickit' ).click( function( event ) {

        if ( event.originalEvent.detail === 1 ) {
            $( '#log' ).append( '(Event:) Single click event received.<br>' );

            /** Is this a true single click or it it a single click that's part of a double click?
             * The only way to find out is to wait it for either a specific amount of time or the `dblclick` event.
             **/
            clickTimeoutID = window.setTimeout(
                    function() {
                        $( '#log' ).append( 'USER BEHAVIOR: Single click detected.<br><br>' );
                    },
                    500 // how much time users have to perform the second click in a double click -- see accessibility note below.
                );

        } else if ( event.originalEvent.detail === 2 ) {
            $( '#log' ).append( '(Event:) Double click event received.<br>' );
            $( '#log' ).append( 'USER BEHAVIOR: Double click detected.<br>' );
            window.clearTimeout( clickTimeoutID ); // it's a dblclick, so cancel the single click behavior.
        } // triple, quadruple, etc. clicks are ignored.

    });

});
</script>

演示:

JSfiddle


关于可访问性和双击速度的注意事项:

  • 维基百科说:“两次连续点击被解释为双击所需的最大延迟没有标准化。”
  • 无法在浏览器中检测系统的双击速度。
  • 似乎在Windows上默认值为500毫秒,范围为100-900毫秒 (来源)
  • 考虑到残疾人士将双击速度设置为最慢的情况。
    • 如果系统的双击速度比上述默认值慢,则会触发单击和双击行为。
    • 不要在同一项上同时使用组合的单击和双击。
    • 或者:在选项中添加一个设置,可以增加该值。

花了一些时间才找到令人满意的解决方案,希望这能有所帮助!


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