【问题标题】:Prevent click event from firing when dblclick event firesdblclick 事件触发时防止触发 click 事件
【发布时间】:2010-10-27 04:58:29
【问题描述】:

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

【问题讨论】:

    标签: javascript dom-events


    【解决方案1】:

    如果其他人偶然发现这个问题(就像我一样)寻找答案,我能想出的绝对最佳解决方案如下:

        $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 次(三次点击),虽然第二次点击没有触发,但第三次点击了

    【讨论】:

    • 这将阻止第二次点击触发,但仍然让第一次点击触发。如果我理解正确,OP 想要在有第二次点击时禁用第一次点击。我认为最好的选择是使用一个小的延迟(我使用的是 200 毫秒,但希望有一个更好的解决方案,我只是看不到)。
    • @ClarkeyBoy - 你错了。如果有第二次点击,这将禁用第一次点击。试试看。
    • 在 IE 11 x64 (Win8.1) 中不起作用:event.detail = 0 始终,单击或双击
    • @robert4 12% 的人还在使用 IE,这太糟糕了。但是,这似乎可行:$node.on('click', function (e) { if (e.originalEvent.detail > 1 || alreadyClicked == true) { alreadyClicked = false; return; } alreadyClicked = true; //Code here. alreadyClicked = false; } 当然,您必须在脚本主体中声明变量并将其设置在文档中。准备好我知道这个问题很老,但它仍然是一个问题。注意:编辑格式并添加上述句子
    • ...但是,正如@ClarkeyBoy 所说,使用这种方法,double 将首先触发详细信息为 1 的点击事件,然后触发详细信息为 2 的第二个点击事件。请参阅jsfiddle。据我所知,这并没有回答 OP。
    【解决方案2】:

    在评论中,你说,

    我将点击处理程序延迟了 300 毫秒(明显且烦人的延迟),甚至......

    所以听起来您想要的是,当您单击时,DOM 应该立即生成一个单击事件,除非单击是双击的第一次单击。

    要实现此功能,当您单击时,DOM 需要能够预测这是最终单击还是第一次双击(但我认为一般来说DOM 来预测用户是否要再次点击)。


    您尝试在单击和双击时采取的两种不同操作是什么? IMO,在普通应用程序中,您可能需要这两个事件:例如单击以聚焦于一个元素,然后双击以激活它。

    当您必须分离事件时,某些应用程序会使用双击以外的方式:例如,它们使用右键单击或 control-click。

    【讨论】:

    • 感谢更新的答案。我认为这真的归结为我需要重新考虑我的 UI。我想要完成的事情真的是不可能的。我应该简单地为这两个动作使用两个控件,或者使用修饰键而不是双击。谢谢!
    【解决方案3】:

    如果您想检测元素被点击了多少次并基于此触发事件,您可以使用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
      }
    });
    

    【讨论】:

    • 这可能有效,但要小心,因为它可能会过时
    【解决方案4】:

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

    【讨论】:

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

    AFAIK DOM Level 2 事件没有指定双击。 它在 IE7 上对我不起作用(令人震惊),但 FF 和 Opera 管理规范没有问题,我可以将所有操作附加到点击事件,但双击只需等到“详细信息”属性事件对象的值为 2。来自文档:“如果在同一屏幕位置发生多次点击,则序列重复,详细属性随着每次重复而递增。”

    【讨论】:

    • 这实际上是一个非常优雅的问题解决方案。第一次点击仍然需要 setTimout/clearTimeout 组合,但它简化了两者之间的协调,甚至为三次点击等打开了大门。谢谢!
    【解决方案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 存储库中添加了此方法的通用版本以及用于触摸设备的 click polyfill,并附有示例:

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

    【讨论】:

      【解决方案7】:

      这是我在模块中所做的区分

             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
      

      【讨论】:

        【解决方案8】:

        如果我有 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>

        【讨论】:

          【解决方案9】:

          我知道这已经很老了,但我想无论如何我都会发布,因为我遇到了同样的问题。以下是我的解决方法。

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

          【讨论】:

            【解决方案10】:

            这是我防止第二次点击的简单解决方案。当然,我可以在检测到双击时重新启动超时,但实际上我从来不需要它。

            clickTimeoutId = null;
            
            onClick(e) {
                if (clickTimeoutId !== null) {
                    // Double click, do nothing
                    return;
                }
            
                // Single click
                // TODO smth
            
                clickTimeoutId = setTimeout(() => {
                    clearTimeout(clickTimeoutId);
                    clickTimeoutId = null;
                }, 300);
            }
            

            【讨论】:

              【解决方案11】:

              总结一下,要识别同一个元素上的 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);
                          
                  }
              }
              

              【讨论】:

                【解决方案12】:

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

                输出:

                【讨论】:

                  【解决方案13】:
                  
                    const toggle = () => {
                        watchDouble += 1;
                        setTimeout(()=>{
                          if (watchDouble === 2) {
                            console.log('double' + watchDouble)
                          } else if (watchDouble === 1) {
                            console.log("signle" + watchDouble)
                          }
                          watchDouble = 0
                        },200);
                  
                    }
                  

                  【讨论】:

                  • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
                  猜你喜欢
                  • 2021-08-15
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-23
                  • 1970-01-01
                  • 2012-04-09
                  相关资源
                  最近更新 更多