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