【问题标题】:Cancel click event in the mouseup event handler在 mouseup 事件处理程序中取消单击事件
【发布时间】:2011-12-27 10:42:15
【问题描述】:

编写一些拖放代码,我想在我的 mouseup 处理程序中取消点击事件。我认为防止默认值应该可以解决问题,但是仍然会触发 click 事件。

有没有办法做到这一点?


这不起作用:

<div id="test">test</div>
<script>
$("#test").mouseup (function (e) {
  var a = 1;
  e.preventDefault();
});
$("#test").click (function (e) {
  var a = 2;
});

【问题讨论】:

  • 点击事件不是发生在鼠标上前吗?
  • 不.. 它发生在 mouseup 之后

标签: javascript jquery click jquery-events mouseup


【解决方案1】:

使用事件捕获阶段

在要取消点击事件的元素周围放置一个元素,并为其添加捕获事件处理程序。

var btnElm = document.querySelector('button');

btnElm.addEventListener('mouseup', function(e){
    console.log('mouseup');
    
    window.addEventListener(
        'click',
        captureClick,
        true // <-- This registeres this listener for the capture
             //     phase instead of the bubbling phase!
    ); 
});

btnElm.addEventListener('click', function(e){
    console.log('click');
});

function captureClick(e) {
    e.stopPropagation(); // Stop the click from being propagated.
    console.log('click captured');
    window.removeEventListener('click', captureClick, true); // cleanup
}
&lt;button&gt;Test capture click event&lt;/button&gt;

JSFiddle Demo

会发生什么:

button 上的点击事件被触发之前,周围的div 上的点击事件被触发,因为它为捕获阶段而不是冒泡阶段注册了自己。

captureClick 处理程序随后停止传播其click 事件并阻止调用按钮上的click 处理程序。正是你想要的。然后它会自行移除以进行清理。

捕获与冒泡:

捕获阶段从 DOM 根到叶子调用,而冒泡阶段从叶子到根调用(参见:wonderful explanation of event order)。

jQuery 总是将事件添加到冒泡阶段,这就是为什么我们需要在这里使用纯 JS 将捕获事件专门添加到捕获阶段。

请记住,IE 在 IE9 中引入了 W3C 的事件捕获模型,因此这不适用于 IE


使用当前的事件 API,您无法在已添加的另一个事件处理程序之前向 DOM 元素添加新的事件处理程序。没有优先级参数和there's no safe cross-browser solution to modify the list of event listeners

【讨论】:

  • 我认为这是最干净的解决方案,我想知道这种技术是否也适用于移动“鬼点击”预防。我很快就会尝试一下,并发表评论。
  • 这正是我想要的。为移动和网络浏览器编写了一个滑动菜单。当用户从网络浏览器拖动菜单时,它也单击了菜单链接。使用这种方法解决了我的问题。尝试了一切,非常感谢!这应该是公认的答案!
  • 点击后可以使用eventListener的once选项去掉eventListener
【解决方案2】:

有办法!

这种方法非常适合我(至少在 chrome 中):

mousedown 上,我将一个类添加到当前正在移动的元素中,并在mouseup 上删除该类。

该类所做的只是设置pointer-events:none

不知何故,这使它工作并且不会触发点击事件。

【讨论】:

  • 这才是真正的解决方案!
  • 我喜欢这种方法。
  • 看起来不错,但在 Chrome 80 上对我不起作用; pointer-events: none 也取消了mouseup...
【解决方案3】:

我的情况的最佳解决方案是:

let clickTime;

el.addEventListener('mousedown', (event) => {
  clickTime = new Date();
});

el.addEventListener('click', (event) => {
  if (new Date() - clickTime < 150) {
    // click
  } else {
    // pause
  }
});

这为用户提供了 150 毫秒的释放时间,如果他们花费的时间超过 150 毫秒,则将其视为暂停,而不是点击

【讨论】:

  • 简单有效!
【解决方案4】:

我遇到了同样的问题,也没有找到解决方案。但我想出了一个似乎有效的技巧。

由于 onMouseUp 处理程序似乎无法取消对带有 preventDefault 或 stopEvent 或任何内容的链接的点击,我们需要让链接自行取消。这可以通过编写一个 onclick 属性来完成,该属性在拖动开始时向 a-tag 返回 false,并在拖动结束时将其删除。

由于 onDragEnd 或 onMouseUp 处理程序是在浏览器解释单击之前运行的,因此我们需要检查拖动结束的位置以及单击的链接等等。如果它在拖动的链接之外结束(我们拖动链接以使光标不再在链接上),我们删除 onDragEnd 中的 onclick 处理程序;但是如果它在光标位于拖动链接上的位置结束(将启动单击),我们让 onclick-handler 自行删除。够复杂了吧?

不是完整的代码,只是为了向您展示这个想法:

// event handler that runs when the drag is initiated
function onDragStart (args) {
  // get the dragged element
  // do some checking if it's a link etc
  // store it in global var or smth

  // write the onclick handler to link element
  linkElement.writeAttribute('onclick', 'removeClickHandler(this); return false;');
}

// run from the onclick handler in the a-tag when the onMouseUp occurs on the link
function removeClickHandler (element) {
  // remove click handler from self
  element.writeAttribute('onclick', null);
}

// event handler that runs when the drag ends
function onDragEnds (args) {
  // get the target element

  // check that it's not the a-tag which we're dragging,
  // since we want the onclick handler to take care of that case
  if (targetElement !== linkElement) {
    // remove the onclick handler
    linkElement.writeAttribute('onclick', null);
  }
}

我希望这能让您了解如何实现这一点。正如我所说,这不是一个完整的解决方案,只是解释这个概念。

【讨论】:

  • 这是迄今为止最智能、最优雅的解决方案!感谢您清楚地解释这个概念。
  • 我想这是最不难看的解决方法:)。我想我只是觉得在这种情况下浏览器的一般行为很烦人。感谢您的帮助。
【解决方案5】:

问题是有一个元素。它需要响应点击。它也需要被拖动。但是,当它被拖动时,它需要在它被放下时不触发点击。

有点晚了,但也许它会帮助别人。制作一个名为“noclick”之类的全局变量并将其设置为 false。拖动项目时,将 noclick 设置为 true。在您的点击处理程序中,如果 noclick 为 true,则将其设置为 false,然后设置 preventDefault、返回 false、stopPropagation 等。这在 Chrome 上不起作用,因为 Chrome 已经具有所需的行为。只需要在浏览器不是 Chrome 时,拖动功能才将 noclick 设置为 true。

您的点击处理程序仍然会被触发,但至少它有办法知道它刚刚从拖动中返回并做出相应的行为。

【讨论】:

  • 什么时候应该取消设置 noclick? click 为我触发 drop (mouseup) 之后。
  • 这似乎对我有用:setTimeout(function() { noClick = false; }, 0);
【解决方案6】:

由于它们是不同的事件,您不能从onmouseup 取消onclick,如果您调用preventDefaultcancelBubble 或其他任何方式,您将停止对onmouseup 事件的进一步处理。 onclick 事件仍处于未决状态,可以这么说尚未被触发。

你需要的是你自己的布尔标志,例如isDragging。您可以在拖动开始时将其设置为 true(例如,在 onmousedown 或其他任何地方)。

但是,如果您直接从onmouseup 将此设置为 false,那么当您收到 onclick 事件 (isDragging == false) 时,您将不再拖动,因为 onmouseuponclick 之前触发。

所以你需要做的是使用一个短暂的超时时间(例如setTimeout(function() {isDragging = false;}, 50);),所以当你的onclick事件被触发时,isDragging仍然是true,你的onclick事件处理程序可以简单地在它做任何其他事情之前拥有if(isDragging) return false;

【讨论】:

  • onmouseup 和 onclick 事件将一起排队,因此不需要超时值,您只需要创建一个超时以允许在 isDragging 设置回 false 之前调用 onclick,但是超时时间可以为 0。
【解决方案7】:

选项#1:

可能最简单的解决方案是Event.stopImmediatePropagation()。虽然这带有一个警告,即同一元素上的所有其他点击事件侦听器在调用时都将被取消,但在这种特定情况下它会起作用。

<div id="test">test</div>
<script>
$("#test").mouseup (function (e) {
  var a = 1;
});

$("#test").click (function (e) {
  // You should add a check here to only stop
  // if the element was actually moved
  e.stopImmediatePropagation();
});

$("#test").click (function (e) {
  var a = 2;
});

作为示例,请参阅此代码笔:
https://codepen.io/guidobouman/pen/bGWeBmZ

选项 #2:

如果您拥有另一个点击处理程序,则另一种(更安全)的解决方案是在事件默认操作被阻止时检查点击处理程序(您想要阻止的处理程序)。

<div id="test">test</div>
<script>
$("#test").mouseup (function (e) {
  var a = 1;
});

$("#test").click (function (e) {
  // You should add a check here to only prevent
  // if the element was actually moved
  e.preventDefault();
});

$("#test").click (function (e) {
  if(e.defaultPrevented) {
    return;
  }

  var a = 2;
});

【讨论】:

    【解决方案8】:
    $(document).mouseup(function(event){
    
        event.preventDefault(); // this prevents only a default action but previously assigned listeners will be called
        event.stopImmediatePropagation() // if there are  others listeners which that shouldn't call 
    }
    

    【讨论】:

    • 您可以发送代码吗?我不知道那里发生了什么,因此很难预测如何解决它:)
    【解决方案9】:

    这可能是可能的,但我不确定您是否可以使用 jQuery 处理这种 evt 管理。 This code example不应该离你的期望太远,或者至少给你一个方向。

    function AddEvent(id, evt_type, ma_fonction, phase) {
        var oElt = document.getElementById(id);
        // modèle W3C mode bubbling
        if( oElt.addEventListener ) {
            oElt.addEventListener(evt_type, ma_fonction, phase);
        // modèle MSIE
        } else if( oElt.attachEvent ) {
            oElt.attachEvent('on'+evt_type, ma_fonction);
        }
        return false;
    }
    
    function DelEvent(id, evt_type, ma_fonction, phase) {
        var oElt = document.getElementById(id);
        // modèle W3C mode bubbling
        if( oElt.removeEventListener ) {
            oElt.removeEventListener(evt_type, ma_fonction, phase);
        // modèle MSIE
        } else if( oElt.detachEvent ) {
            oElt.detachEvent('on'+evt_type, ma_fonction);
        }
        return false;
    }
    
        var mon_id = 'test';
        var le_type_evt = "mouseup";
        var flux_evt = false; // prevent bubbling
        var action_du_gestionnaire = function(e) {
            alert('evt mouseup on tag <div>');
    
            // 1ère méthode : DOM Lev 2
            // W3C
                if ( e.target )
                    e.target.removeEventListener(le_type_evt, arguments.callee, flux_evt);
            // MSIE
                else if ( e.srcElement )
                    e.srcElement.detachEvent('on'+le_type_evt, arguments.callee);
    
            // 2ème méthode DOM Lev2
            // DelEvent(mon_id, le_type_evt, action_du_gestionnaire, flux_evt);
        };
    
    
        AddEvent(mon_id, le_type_evt, action_du_gestionnaire, flux_evt);
    

    【讨论】:

      【解决方案10】:

      我最近遇到了同样的问题。这是我的解决方案:)

          initDrag: function (element) {
              var moved = false,
                  target = null;
      
              function move(e) {
                  // your move code
                  moved = true;
              };
      
              function end(e) {
                  var e = e || window.event,
                      current_target = e.target || e.srcElement;
      
                  document.onmousemove = null;
                  document.onmouseup = null;
      
                  // click happens only if mousedown and mouseup has same target
                  if (moved && target === current_target) 
                      element.onclick = click;
      
                  moved = false;
              };
      
              function click(e) {
                  var e = e || window.event;
      
                  e.preventDefault();
                  e.stopPropagation();
      
                  // this event should work only once
                  element.onclick = null;
              };
      
              function init(e) {
                  var e = e || window.event;
                  target = e.target || e.srcElement;
      
                  e.preventDefault();
      
                  document.onmousemove = move;
                  document.onmouseup = end;
              };
      
              element.onmousedown = init;
          };
      

      【讨论】:

        【解决方案11】:

        这是我拖动并单击同一元素的解决方案。

        $('selector').on('mousedown',function(){
          setTimeout(function(ele){ele.data('drag',true)},100,$(this));
        }).on('mouseup',function(){
          setTimeout(function(ele){ele.data('drag',false)},100,$(this));
        }).on('click',function(){
          if($(this).data('drag')){return;}
          // put your code here
        });
        

        【讨论】:

          【解决方案12】:

          我使用的解决方案如下:

          var disable_click = false;
          
          function click_function(event){
              if (!disable_click){
                  // your code
              }
          }
          
          function mouse_down_function(event){
              disable_click = false; // will enable the click everytime you click
              // your code
          }
          
          function mouse_up_function(event){
              // your code
          }
          
          function mouse_drag_function(event){
              disable_click = true; // this will disable the click only when dragging after clicking
             // your code
          }
          

          根据名称将每个函数附加到相应的事件!

          【讨论】:

            【解决方案13】:

            我的解决方案不需要全局变量或超时或更改 html 元素,只需要 jquery,这在纯 js 中肯定有一些等效项。

            我为 onClick 声明了一个函数

            function onMouseClick(event){
                 // do sth
            }
            

            我为 MouseDown 声明了一个函数(你也可以在鼠标向上做同样的事情)来决定是否处理 onclick 事件

            function onMouseDown(event){
                 // some code to decide if I want a click to occur after mouseup or not
                if(myDecision){
                    $('#domElement').on("click", onMouseClick);
                }
                else $('#domElement').off("click");
            } 
            

            快速提示:您应该确保
            $('#domElement').on("click", onMouseClick); 不会被多次执行。在我看来,如果它是 onMouseClick 也会被多次调用。

            【讨论】:

            • 除了“myDecision”将基于全局变量(或拖放过程的某些全局状态)这一事实:)
            【解决方案14】:

            你的做法很简单,你给所有对象添加一个 addEventListener 并监听“mousedown”和“mouseup”事件。 如果你的'mousedown'事件函数被调用,你会显示一个带有'style.position =“absolute”的DIV窗口,必须是透明的,并且占据整个窗口。并将 Z-Order 设置为 9999999 之类的值。如果将 DIV 隐藏在“MouseLeave”处,则 Click 可能会引发。所以也听听创建的 DIV 它的 mousedown 并隐藏 div 也只是为了确定。 然后,您要抑制的对象的“单击”事件将不会引发。 在“mouseup”中,您再次隐藏创建的 div。

            此技巧仅用于编写带有 WebView2 对象的应用程序,您无法控制所显示的 html 网站,但将 javascript 注入其中。 除此之外,如果您可以控制 html 源代码,只需在用于取消或不取消的对象的 mousedown 中设置一个 var,然后在“点击”事件中您只需查看 var 做什么。

            【讨论】:

            • 嗨@Michiel,欢迎来到SO!只是对您回答的评论:向网页的所有对象添加事件处理程序可能会显着降低性能。想想有成百上千个元素的网页......
            【解决方案15】:
            $(document).mouseup(function(event){ // make sure to set the event parameter
                event.preventDefault(); // prevent default, like you said
            });
            

            需要注意的重要一点是event 参数。

            编辑:你想取消拖动吗?

            我知道这样做的方法是使用bind()(对于较旧的jQuery版本)或on()(对于jQuery 1.7+)附加mousedown事件,然后分别使用unbind()off()拆下来。

            $(document)
                .on("mousedown", function(){...})
                .on("mouseup", function(){
                    $(document).off("mousedown");
                });
            

            【讨论】:

            • 我不想取消拖动,如果发生拖动我想取消点击。
            【解决方案16】:

            如果您希望抑制单击事件,您可以在 click 事件处理程序中添加语句 event.preventDefault() 而不是 mouseup 事件处理程序。使用下面的代码你会得到它的工作。

            <div id="test">test</div>
            <script>
            $("#test").mouseup (function (e) {
              var a = 1;
            
            });
            $("#test").click (function (e) {
             e.preventDefault();
              var a = 2;
            });
            

            希望这能解决您的问题。

            【讨论】:

            • 不是真的。我希望点击事件处理程序根本不被调用。
            • “我想取消 mouseup 处理程序中的点击事件” - 非常清楚。
            猜你喜欢
            • 1970-01-01
            • 2015-12-22
            • 2015-01-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-08-30
            相关资源
            最近更新 更多