Javascript 在 mouseup 事件处理程序中取消 click 事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8643739/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:52:56  来源:igfitidea点击:

Cancel click event in the mouseup event handler

javascriptjqueryclickjquery-eventsmouseup

提问by Yaron

Writing some drag&drop code, I'd like to cancel the click events in my mouseup handler. I figured preventing default should do the trick, but the click event is still fired.

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

Is there a way to do this?

有没有办法做到这一点?



This doesn't work:

这不起作用:

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

采纳答案by Fredrik Bostr?m

I had the same problem and didn't found a solution either. But I came up with a hack that seems to work.

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

Since the onMouseUp handler doesn't seem to be able to cancel the click on a link with preventDefault or stopEvent or anything, we need to make the link cancel itself. This can be done by writing an onclick attribute which returns false to the a-tag when the drag begins, and removing it when the drag ends.

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

And since the onDragEnd or onMouseUp handlers are run before the click is interpreted by the browser, we need to do some checking where the drag ends and which link is clicked and so on. If it ends outside the dragged link (we dragged the link so that the cursor isn't on the link anymore), we remove the onclick handler in the onDragEnd; but if it ends where the cursor is on the dragged link (a click would be initiated), we let the onclick-handler remove itself. Complicated enough, right?

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

NOT COMPLETE CODE, but just to show you the idea:

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

// 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);
  }
}

I hope this gives you an idea of how this can be accomplished. As I said, this is not a complete solution, just explaining the concept.

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

回答by flu

Use the event capture phase

使用事件捕获阶段

Put an element around the element you want to cancel the click event for, and add a capture event handler to it.

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

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
}
<button>Test capture click event</button>

JSFiddle Demo

JSFiddle 演示

What happens:

发生什么了:

Before the click event on the buttonis triggered the click event on the surrounding divgets fired because it registered itself for the capture phase instead of the bubbling phase.

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

The captureClickhandler then stops the propagation of it's clickevent and prevents the clickhandler on the button to be called. Exactly what you wanted. It then removes itself for cleanup.

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

Capturing vs. Bubbling:

捕获与冒泡:

The capture phase is called from the DOM root up to the leafs while the bubbling phase is from the leafs up the root (see: wonderful explanation of event order).

捕获阶段从 DOM 根到叶子调用,而冒泡阶段从叶子到根调用(参见:事件顺序的精彩解释)。

jQuery always adds events to the bubbling phase that's why we need to use pure JS here to add our capture event specifically to the capture phase.

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

Keep in mind, that IE introduced the W3C's event capturing model with IE9 so this won't work with IE < 9.

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



With the current Event API you can't add a new event handler to a DOM Element before another one that was already added. There's no priority parameter and there's no safe cross-browser solution to modify the list of event listeners.

使用当前的事件 API,您无法在已添加的另一个事件处理程序之前向 DOM 元素添加新的事件处理程序。没有优先级参数,也没有安全的跨浏览器解决方案来修改事件侦听器列表

回答by FDIM

There is a solution!

有一个解决方案!

This approach works for me very well (at least in chrome):

这种方法对我很有效(至少在 chrome 中):

on mousedownI add a class to the element that is currently being moved and on mouseupI remove the class.

mousedown我加个班,以当前正在移动的元素,在mouseup我删除类。

All that class does is sets pointer-events:none

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

Somehow this makes it work and click event is not fired.

不知何故,这使它起作用,并且不会触发单击事件。

回答by Kaylakaze

The problem is there's an element. It needs to respond to clicks. It also needs to be dragged. However, when it's dragged, it needs to not trigger click when it is dropped.

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

A little late, but maybe it'll help someone else. Make a global variable named "noclick" or something and set it to false. When dragging the item, set noclick to true. In your click handler, if noclick is true, set it to false and then preventDefault, return false, stopPropagation, etc. This won't work on Chrome though since Chrome already has the desired behavior. Just make it so that the drag function only sets noclick to true if the browser isn't Chrome.

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

Your click handler will still get fired, but at least it has a way to know that it just came back from drag and behave accordingly.

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

回答by Kim T

The best solution for my situation was:

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

let clickTime;

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

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

This gives the user 150ms to release, if they take longer than 150ms it's considered a pause, rather than a click

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

回答by Lee Kowalkowski

As they are different events, you cannot cancel onclickfrom onmouseup, if you call preventDefaultor cancelBubble, or whatever, you are stopping the onmouseupevent from being processed any further. The onclickevent is still pending, yet to be fired, so to speak.

由于它们是不同的事件,因此您无法onclick从取消onmouseup,如果您调用preventDefaultcancelBubble或其他任何方式,您将停止onmouseup进一步处理该事件。onclick可以这么说,该事件仍在等待中,尚未被解雇。

What you need is your own boolean flag, e.g. isDragging. You can set this to true when dragging starts (e.g. within onmousedown, or whatever).

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

But if you reset this to false directly from onmouseup, you will not be dragging any more when you receive your onclickevent (isDragging == false), because onmouseupfires before onclickdoes.

但是,如果您直接从onmouseup将其重置为 false ,则在收到onclick事件 ( isDragging == false)时将不再拖动,因为在onmouseup触发之前onclick会触发。

So what you need to do is use a short timeout (e.g. setTimeout(function() {isDragging = false;}, 50);), so when your onclickevent is fired, isDraggingwill still be true, and your onclickevent handler can simply have if(isDragging) return false;before it does anything else.

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

回答by starl1ng

i recently faced with the same problem. Here's my solution :)

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

    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;
    };

回答by Tom

My solution doesn't require global variables or timeouts or changing html elements just jquery which surely has some aquivalent in plain js.

我的解决方案不需要全局变量或超时或更改 html 元素只是 jquery,它肯定在普通 js 中有一些等价物。

I declare a function for onClick

我为 onClick 声明了一个函数

function onMouseClick(event){
     // do sth
}

I declare a function for MouseDown (u may also do the same in mouse up) to decide if to handle an onclick event or not

我为 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");
} 

Quick Note: you should make sure that
$('#domElement').on("click", onMouseClick);is not executed multiple times. Seems to me that in case it is the onMouseClick will be called multiple times too.

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

回答by hornetbzz

It might be possible but I'm not sure if you can handle this kind of evt management with jQuery. This code exampleshould not be far away from your expectation or at least give you a direction.

这可能是可能的,但我不确定您是否可以使用 jQuery 处理这种 evt 管理。这个代码示例不应该与您的期望相去甚远,或者至少给您一个方向。

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);

回答by Quy?n Lê

This is my solution for drag and click on same element.

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

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