Cancel click event in the mouseup event handler

自古美人都是妖i 提交于 2020-01-10 00:27:11

问题


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

回答1:


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.

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?

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.




回答2:


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.




回答3:


There is a solution!

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

on mousedown I add a class to the element that is currently being moved and on mouseup I remove the class.

All that class does is sets pointer-events:none

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




回答4:


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.

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.




回答5:


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




回答6:


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.




回答7:


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

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



回答8:


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



回答9:


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



回答10:


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 !




回答11:


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.




回答12:


$(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 
}



回答13:


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



回答14:


If you wish suppress the click event you have add the statement event.preventDefault() in the click event handler only and not the mouseup event handler. Use the below code you will get it working.

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

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

Hope this solves your problem.



来源:https://stackoverflow.com/questions/8643739/cancel-click-event-in-the-mouseup-event-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!