Cancel click event in the mouseup event handler

2019-02-07 21:35发布

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.

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

14条回答
贪生不怕死
2楼-- · 2019-02-07 21:44

The best solution for my situation was:

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

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

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

查看更多
聊天终结者
3楼-- · 2019-02-07 21:44
$(document).mouseup(function(event){ // make sure to set the event parameter
    event.preventDefault(); // prevent default, like you said
});

The important thing to note is the event parameter.

EDIT: You want to cancel the drag?

The way I know to do this is to either use bind() (for older jQuery versions) or on() (for jQuery 1.7+) to attach the mousedown event, then use unbind() or off() respectively to detach it.

$(document)
    .on("mousedown", function(){...})
    .on("mouseup", function(){
        $(document).off("mousedown");
    });
查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-02-07 21:46

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

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

But if you reset this to false directly from onmouseup, you will not be dragging any more when you receive your onclick event (isDragging == false), because onmouseup fires before onclick does.

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

查看更多
Luminary・发光体
5楼-- · 2019-02-07 21:48

the solution that i use is the following:

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
}

attach each function to the appropriate event according to the name !

查看更多
\"骚年 ilove
6楼-- · 2019-02-07 21:48

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

I declare a function for 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

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.

查看更多
\"骚年 ilove
7楼-- · 2019-02-07 21:52

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

What happens:

Before the click event on the button is triggered the click event on the surrounding div gets fired because it registered itself for the capture phase instead of the bubbling phase.

The captureClick handler then stops the propagation of it's click event and prevents the click handler on the button to be called. Exactly what you wanted. It then removes itself for cleanup.

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

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.

Keep in mind, that IE introduced the W3C's event capturing model with IE9 so this won't work with 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.

查看更多
登录 后发表回答