I have a click event handler and a mousedrag event handler for a Paper.js Item.
item.on('click', function(event) {
event.stopPropagation();
event.preventDefault();
//...
});
item.on('mousedrag', function(event) {
event.stopPropagation();
event.preventDefault();
//...
});
Whenever I cause a mousedrag event a click event is also generated. How can I prevent this?
When the mouse is clicked then released a click event is generated. When the mouse is held down and dragged, drag events are generated. I generally implement this by setting events on 'mousedown' and 'mouseup' in addition to 'mousedrag'.
Playing around I've found that if you return false from your 'mousedrag' event handler it will suppress further events, i.e., neither 'mouseup' nor 'click' will be invoked. Here's a sketch illustrating mouse events. If you uncomment the return false in the 'mousedrag' handler you will not get the 'mouseup' or 'click' events.
The following is example code for how I usually implement it - I didn't discover that returning false from the handler suppressed further processing until looking at the code tonight.
var drag;
view.on('mousedown', function(e) {
drag = false;
});
view.on('mousedrag', function(e) {
drag = true;
// do whatever else you need to when dragging the mouse
});
view.on('mouseup', function(e) {
// see if it was just a click and handle that
if (drag) {
// handle the end of a drag
} else {
// do whatever a click down/up without drag needs to do
}
});
Related
I am new to javascript and am stuck with an issue
I am not able to prevent click on an element while dragging. What do i need to do in my event handlers to prevent click on dragging/ note click should work as normal while element is not being dragged
I tried prevent default and stop propagation inside the dragstart event handler, but this seems to disable the drag action and not the click action
this.message_list
.addEventListener('initrow', function(o) { ref.init_message_row(o); })
.addEventListener('dblclick', function(o) { ref.msglist_dbl_click(o); })
.addEventListener('keypress', function(o) { ref.msglist_keypress(o); })
.addEventListener('select', function(o) { ref.msglist_select(o); })
.addEventListener('dragstart', function(o) {alert('33'); ref.drag_start(o);})
.addEventListener('dragstart', function(e) {alert('44'); ref.drag_start(e); })
alerts 33 and 44 fire on dragstart , but adding stuff like stopping event propagation or preventing default action, disables the drag and not the click.
Once again click action is needed on the same element when it is not being dragged
I'm having trouble understanding why the setgeometry event inside my mouseup event keeps firing after rightlick event... even though I do remove the setgeometry event listener.
drawLayer.addListener('mouseup', function() {
changedGeom = drawLayer.addListener('setgeometry', function(e) {
console.log('got new geometry');
google.maps.event.removeListener(changedGeom);
});
});
drawLayer.addListener('rightclick', function(e) {
console.log('deleted polygon');
google.maps.event.removeListener(changedGeom);
});
The reason why I have setgeometry inside mouseup is because I only want the last position of the polygon. Otherwise it will record every move.
Maybe I'm over complicating this, but here's what to do to reproduce the problem:
Open the console and drag the polygon on the Google Map. You should see got new geometry message once.
Now rightclick on your mouse and you should see the message delete polygon.
Now move the polygon again. You will see that the setgeometry event keeps firing, resulting in plenty of got new geometry messages.
I'm unsure why this is happening. Can someone explain?
Demo: http://jsfiddle.net/8ueq6spu
I have figured out why this is happening to you.
I removed your rightclick listener to see that an event was still being triggered on a right click action, then I realised it is because both left click and right click result in a mouseup event.
So in your current implementation the listener is being removed on the rightclick press/event, but a new instance is created as soon as the button is released due to the mouseup event.
To fix this behaviour you could opt for a standard click instead of mouseup, however, I also noted that this event does not fire when you drag the boundary nodes (which is probably why you used mouseup in the first place).
A quick fix would be to still use the mouseup event, but detect which type of click the event originated from e.g.
drawLayer.addListener('mouseup', function(e) {
if (e.which === 1) {
console.log("Left click");
} else if (e.which === 2) {
console.log("Middle click");
} else if (e.which === 3) {
console.log("Right click");
}
});
Adapted this solution from : How to distinguish between left and right mouse click on mousedown
event in Google Map API v3
I'd like to cancel the MouseEvent that is fired on object:moving in fabric.js, to prevent all the actions when some condition is met. I tried to set cancelBubble = true or simply return false; but with no success. Any ideas?
Sample fiddle with some event:
http://jsfiddle.net/fabricjs/S9sLu/
Mouse mouse:moving event is continuosly fired while mouse pointer is on canvas. But by checking event.target you can check if that's a generic or object related move.
canvas.on('mouse:move', function(options) {
if(options.target) {
//suppress event handlers here
}
});
Here's a JSFiddle of the behavior I'm seeing, relating to middle-click and the click event in Chrome and FF.
'click' kinda sorta works
Approach 1: Bind a click handler directly to an a element and a middle-click will trigger the handler in Chrome but not in FF.
$('div a').on('click', function(ev) {
// middle click triggers this handler
});
Approach 2: Bind a delegated click handler to a div which contains one or more a. Middle click will not trigger this handler in Chrome or FF.
$('div').on('click', 'a', function(ev) {
// middle click doesn't trigger this handler
});
This approach is extremely valuable if the div starts out empty and the a elements are filled in later by an AJAX call, or as a result of some user input.
'mouseup' works
Using mouseup instead of click causes both approach 1 and 2 to work in both browsers.
// Approach 1 w/ mouseup
$('div a').on('mouseup', function(ev) {
// middle click **does** trigger this handler in Chrome and FF
});
// Approach 2 w/ mouseup
$('div').on('mouseup', 'a', function(ev) {
// middle click **does** trigger this handler in Chrome and FF
});
Here's the JSFiddle with mouseup.
This is interesting and might be useful in some cases, because mouseup is almost click. But mouseup isn't click, and I'm after the behavior of click. I do not want to create a hacky mousedown; setTimeout; mouseup simulation of click.
I'm pretty sure the answer is "nope", but is there a cross-browser way to cause middle-click to trigger click handlers? If not, what are the reasons why?
The click event is generally fired for the left mouse button, however, depending on the browser, the click event may or may not occur for the right and/or middle button.
In Internet Explorer and Firefox the click event is not fired for the right or middle buttons.
Therefore, we cannot reliably use the click event for event handlers on the middle or right button.
Instead, to distinguish between the mouse buttons we have to use the mousedown and mouseup events as most browsers do fire mousedown and mouseup events for any mouse button.
in Firefox and Chrome event.which should contain a number indicating what mouse button was pressed (1 is left, 2 is middle, 3 is right).
In Internet Explorer on the other hand, event.button indicates what mouse button was clicked (1 is left, 4 is middle, 2 is right);
event.button should also work in Firefox and other browsers, but the numbers can be slightly different (0 is left, 1 is middle, 2 is right).
So to put that together we usually do something like this :
document.onmousedown = function(e) {
var evt = e==null ? event : e;
if (evt.which) { // if e.which, use 2 for middle button
if (evt.which === 2) {
// middle button clicked
}
} else if (evt.button) { // and if e.button, use 4
if (evt.button === 4) {
// middle button clicked
}
}
}
As jQuery normalizes event.which, you should only have to use that in jQuery event handlers, and as such be doing:
$('div a').on('mousedown', function(e) {
if (e.which === 2) {
// middle button clicked
}
});
In other words you can't use the onclick event, so to simulate it you can use both mousedown and mouseup.
You can add a timer to limit the time allowed between the mousedown and mouseup event, or even throw in a mousemove handler to limit the movement between a mousedown and mouseup event, and make the event handler not fire if the mouse pointer moved more than ten pixels etc. the possibilites are almost endless, so that shouldn't really be an issue.
$('#test').on({
mousedown: function(e) {
if (e.which === 2) {
$(this).data('down', true);
}
},
mouseup: function(e) {
if (e.which === 2 && $(this).data('down')) {
alert('middle button clicked');
$(this).data('down', false);
}
}
});
Short answer: Nope.
The question is, what do you want to capture the middle clicks for? A middle click isn't meant to interact with the current page but rather to open a link in a new tab.
Chrome is also currently working on droping this behavior: https://code.google.com/p/chromium/issues/detail?id=255
And there is currently a general discussion on the w3c mailing list about this topic: http://lists.w3.org/Archives/Public/www-dom/2013JulSep/0203.html
Yet for now, you can catch middleclicks in Firefox on a document-level:
$(document).on('click', function(e){
console.log(e);
});
I've build a factory for creating Middle mouse click handlers using vanilla JS and working in latest Firefox and Chrome:
const MiddleClickHandlerFactory = (node, handlerFn) => {
node.addEventListener('mousedown', e => {
if (e.button !== 1) return;
e.preventDefault(); // stop default scrolling crap! Instead install ScrollAnywhere!
const originalTarget = e.target;
document.addEventListener('mouseup', e => { // register on DOCUMENT to be sure it will fire even if we release it somewhere else
if (e.target.isSameNode(originalTarget)) handlerFn(e);
}, {capture: true, once: true, passive: true});
}, true)
};
I'm trying to create a file drag/drop handler (drag a file into the browser window, to be used for upload).
For some reason when I bind the drag/drop listener to $("body") instead of to a $("div") in the body the events fire several times in a row, sometimes even non-stop (seemingly looping). What could be causing this?
Here's a trimmed down version of the code: http://jsfiddle.net/WxMwK/9/
var over = false;
$("body")
.on("dragover", function(e){
e.preventDefault();
if (! over) {
over = true;
$("ul").append($("<li/>").text("dragover"));
}
})
.on("dragleave", function(e){
e.preventDefault();
if (over) {
over = false;
$("ul").append($("<li/>").text("dragleave"));
}
})
.on("drop", function(e){
e.preventDefault();
if (over) {
over = false;
$("ul").append($("<li/>").text("drop"));
}
});
To test: drag a file into the orange area, you'll see the event firing multiple times in a row.
The anon is (mostly) correct. To put it simply: when the mouse moves over the edge of an element inside your drop target, you get a dropenter for the element under the cursor and a dropleave for the element that was under the cursor previously. This happens for absolutely any descendant.
You can't check the element associated with dragleave, because if you move the mouse from your drop target onto a child element, you'll get a dropenter for the child and then a dropleave for the target! It's kind of ridiculous and I don't see how this is a useful design at all.
Here's a crappy jQuery-based solution I came up with some time ago.
var $drop_target = $(document.body);
var within_enter = false;
$drop_target.bind('dragenter', function(evt) {
// Default behavior is to deny a drop, so this will allow it
evt.preventDefault();
within_enter = true;
setTimeout(function() { within_enter = false; }, 0);
// This is the part that makes the drop area light up
$(this).addClass('js-dropzone');
});
$drop_target.bind('dragover', function(evt) {
// Same as above
evt.preventDefault();
});
$drop_target.bind('dragleave', function(evt) {
if (! within_enter) {
// And this makes it un-light-up :)
$(this).removeClass('js-dropzone');
}
within_enter = false;
});
// Handle the actual drop effect
$drop_target.bind('drop', function(evt) {
// Be sure to reset your state down here
$(this).removeClass('js-dropzone');
within_enter = false;
evt.preventDefault();
do_whatever(evt.originalEvent.dataTransfer.files);
});
The trick relies on two facts:
When you move the mouse from a grandchild into a child, both dragenter and dragleave will be queued up for the target element—in that order.
The dragenter and dragleave are queued together.
So here's what happens.
In the dragenter event, I set some shared variable to indicate that the drag movement hasn't finished resolving yet.
I use setTimeout with a delay of zero to immediately change that variable back.
But! Because the two events are queued at the exact same time, the browser won't run any scheduled functions until both events have finished resolving. So the next thing that happens is dragleave's event handler.
If dragleave sees that it was paired with a dragenter on the same target element, that means the mouse must have moved from some descendant to some other descendant. Otherwise, the mouse is actually leaving the target element.
Then the setTimeout finally resolves zero seconds later, setting back the variable before another event can come along.
I can't think of a simpler approach.
You are adding a listener on the BODY HTMLElement for the dragover, dragleave and drop.
When you continue to drag over the DIV, there is a dragleave that is fired because the mouse is no more dragging over the BODY, but over the DIV.
Secondly, as you are not stopping the bubble event on the DIV (no listener is set), the dragover fired on the DIV is bubling to the BODY.
If I resume:
The mouse enter the body (in dragover)
--> fire drag over (body)
The mouse enter the DIV in the body
--> fire drag leave (of BODY)
--> fire drag over (of DIV) --> event bubling --> fire drag over (of BODY)
There is a similar problem with mouseover and mouseout, which is fixed by using mouseenter and mouseleave.
May be you can try the same code using dragenter event type. If its not working, you can check if the event.target is the BODY. This test could help to skip undesired drag event.
Good luck
var over = false;
$("body")
.on("dragover", function(e){
e.preventDefault();
if (! over) {
over = true;
$("ul").append($("<li/>").text("dragover"));
}
})
.on("dragleave", function(e){
e.preventDefault();
if (over) {
over = false;
$("ul").append($("<li/>").text("dragleave"));
}
})
.on("drop", function(e){
e.preventDefault();
if (over) {
over = false;
}
});
Or you could just use stop(); to stop animation buildup