Events-- 'mouseup' not firing after mousemove - javascript

I am trying to drag an image with Javascript (no libraries). I am able to listen to mousedown and mousemove events. For some reason, I am not able to capture the mouseup event after mousemove. (I can capture mouseup if it is a click but not if it is a drag).
I have tried to listen to the event on document, window, and the image.
Here's the url to my test page:
https://dl-web.dropbox.com/get/Public/move.html?w=74a0d498
Any help on this would be greatly appreciated!

Found the issue, if it is going to be of help to anyone:
I added event.preventDefault(); in the mousedown event and now I am getting mouseup notifications.

Strangely, I've found that when I set my text as unselectable using the below CSS, that inhibits the mouseup event from firing as well -- perhaps this will help someone else.
-moz-user-select: none;
-khtml-user-select: none;
user-select: none;

I have seen the mouseup event not fire on my target div because its parent div began consuming drag events. I clicked my target div and this caused my mousedown and mousemove handlers to run. I was expecting to see a mouseup but I did not.
In my case, I had a parent div living beneath my target div in the same location where I launched my mouse button click, and instead of bubbling up the mouseup event on my document once I let go of the left mouse button, I instead got a dragend event fire on its parent div.
The solution for me was simply to set user-select: none; CSS property on the parent div which houses my target div and to make sure I set user-select: text on my target div. This property seems to disable dragging for my parent div and because it has stopped consuming drag events, my mouseup event now properly bubbles its way up.
I presume the reason why this might happen is because the browser starts thinking your mouse move is actually part of a drag event on another element, at which point it seems to stop reporting the standard mouse events and switches to drag events instead.

I was running into this exact same issue! Adding event.preventDefault(); worked for me but I was forced to add it to both the mousedown and mousemove functions.

Neither of the above answers reliably worked to ensure a mouseup event.
Here's what I discovered works consistently:
document.querySelector('html').addEventListener('mouseup', function (e) {
console.log("html mouseup");
var evt = document.createEvent("MouseEvents");
evt.initEvent("mouseup", true, true);
document.getElementById('drag-me').dispatchEvent(evt);
});
If mouseup fires on target element, it does not fire on html, and if it did not fire on target, it will fire on html.

For above two methods:
add e.preventDefault() in mouseup event
add user-select: none CSS rule
Tried on Chrome 87 but both useless. Finally I add a additional mouseout event listener and it fired when drag out of element.

The event which you want to fire in mouseup , You can fire in mousedown and inside the function write event.stopPropagation().
It will stop the drag event.

It is going to be of help to anyone:
I recommended you use window instead of document when adding Event mouseup

The problem is in selection. Some elements are draggable, when user - having unknowingly uncollapsed selection (likely across many non-text elements), starts moving his mouse with mouse button pushed down he drags these elements alongside the movement of his mouse.
Try setting draggable="false" on html elements where mousedown event starts or if it doesn't work try clearing or collapsing selection on mousedown.
Clear:
getSelection().setPosition(null);
A bit cleaner, collapse:
const selection = getSelection();
selection.setPosition(selection.anchorNode, selection.anchorOffset)
Setting draggable="false" worked for me when working with mousedown event starting with an <img> element and clearing selection worked when I had selections spanning over multiple elements on a control panel an this made some input[type="range"] elements unusable (click event worked but not thumbnail dragging).

Related

jQuery clone html element dynamically on touchstart and then receive touchmove events

I have some elements (widgets) in a div named tools. When I click on those elements I want to drag and drop them but not them directly. I clone the element in a new item named newnode and this is what I want to drag around the editor.
The trick that is working on desktop is to trigger an event on the new element, just $("#newnode").trigger(e);
$("#tools").on("mousedown touchstart",".widgets", function( e ) {
InitialX=$(this).offset().left;
InitialY=$(this).offset().top;
$("#newnode").css("left",InitialX);
$("#newnode").css("top",InitialY);
$("#newnode").css("width",$(this).width());
$("#newnode").css("height",$(this).height());
$("#newnode>svg").html($($(this).find("g")[0]).clone());
$("#newnode").show();
$("#newnode").trigger(e);
});
The code is working fine for desktop/computers but not for mobiles. When I send the trigger on desktop, the new cloned items start to receive events, being the new focused item, but when I do the same on mobiles tools/widget elements keeps receiving the events
I also added this code to see what was happening with the events, and yes, the old events is being sent to the non-cloned item.
$("#tools").on("blur focus focusin focusout load resize scroll unload click " +
"dblclick mousedown mouseup mousemove mouseover mouseout mouseenter " +
"mouseleave change select submit keydown keypress keyup error" +
"touchstart touchend touchmove", ".widgets",function(e){
console.log(e.type);
});
#newnode has z-index: 100000; to force to be over the original element.
to see an example on jsfiddle:
https://jsfiddle.net/hamboy75/v0br1cq6/44/
If you on computer, change to mobile in inspector, and you will see how it is different in log. Just click over a menu (duplicated will appear in gray color), without unclicking move a bit the mouse.
When viewed as computer newnode will receive events. When viewed as mobile widget will receive the events.
Finally i found a solution that works for my needs even if it is not exactly what i wanted (i wanted to receive all events in the new cloned item). Instead of adding the next line to receive the events in the #newnode element
$("#newnode").trigger(e);
i use the next code to receive events on #tools>.widgets, and if #newnode is visible resend the events to it using trigger.
$("#tools").on("mouseup mousedown mousemove touchstart touchend touchmove touchcancel", ".widgets",function(e){
if($("#newnode").is(":visible"))
$("#newnode").trigger(e);
});

Is it possible for an element to only listen to click events and ignore events like mouse movement?

My problem is the following: I have a div, which has some crucial mouse events I need to fetch. These are onMouseOver to be more precise.
However, when the mouse hover over this div, I am creating a few buttons on this div, which are then leading to misbehavious of my onMouseOver event, as they are overlaying the div, and as soon as the mouse is being moved onto those buttons, it "leaves" the div and the event stops triggering.
So, I want to be able to ignore mouse movement on these buttons, but I still want to grab the click event when someone clicks the buttons, of course.
The closest I've found so far was
pointer-event: none
, but that also disables click events.
Is there something else I could use to achieve this?
Thanks in advance
You should use mouseenter and mouseleave events if using jQuery. mouseover and mouseout functions do not bubble from child to parent, so you end up triggering a mouseout event when you hover the child elements.
You can always attach onmouseover event to these buttons and put in the same function as for div.
Even if You ignores onmouseover on this buttons You will lost onmouseover on the div.
You can use preventdefault(). after that you can write your own code
$("a").click(function(e){
e.preventDefault();
//You can write other code what you want to write
});
This code may help you to get override the other code
or you may use e.stopPropagation() in stead of e.preventDefault(); it will stop all other active events

Internet Explorer leaks click event after adding an overlay in a jQuery mousedown handler

In a mousedown event-handler of a div another new div is created and appended to the body.
This new div has position:fixed (can also be position:absolute) and has 100% width and 100% height. Therefore it immediately covers the source div which triggered the mouse down event.
Now with the latest Google Chrome (v30), latest Firefox (v24), Opera v12.16 and even with a older Safari v5.1.1 (on Windows) after the mousedown event no click event gets fired on an event listener attached to the body.
Only Internet Explorer (both 9 and 10) does fire the click event on the body afterwards! Why? And how can this be prevented? Is this actually a bug in IE?
The HTML:
<div class="clickme">Click me</div>
The CSS:
.clickme {
background-color: #BBB;
}
.overlay {
position: fixed; /* or absolute */
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: #000;
}
The JavaScript:
$(document).on('click', function(event) {
console.log('body click');
});
$('.clickme').on('mousedown', function(event) {
console.log('div mousedown');
var mask = $('<div></div>');
mask.attr('class', 'overlay');
mask.appendTo('body');
});
Here is a the example with some additional comments: http://jsfiddle.net/Fh4sK/5/
After clicking the "Click me" div, only
div mousedown
should be written to the console, but in Internet Explorer it actually is
div mousedown
body click
I appreciate any help!
EDIT 1:
I found some resources describing the conditions when to trigger a click event:
http://www.quirksmode.org/dom/events/click.html:
"click - Fires when a mousedown and mouseup event occur on the same element."
http://www.w3.org/TR/DOM-Level-3-Events/#events-mouseevent-event-order
"...in general should fire click and dblclick events when the event target of the associated mousedown and mouseup events is the same element with no mouseout or mouseleave events intervening, and should fire click and dblclick events on the nearest common ancestor when the event targets of the associated mousedown and mouseup events are different."
I'm not 100% sure what the "correct" behaviour now actually should be (maybe IE is the only browser which handles it right?). From the last sentence, it seems that it is correct to fire the click event on the body, because the body is the "nearest common ancestor" of both div elements. There are some other statements on the referenced w3.org page above, which describe the behaviour if an element gets removed, but again I'm not sure if this applies here, as no element gets removed, but covered by an other element.
EDIT 2:
#Evan opened a bug report asking Microsoft to drop the described behaviour: https://connect.microsoft.com/IE/feedback/details/809003/unexpected-click-event-triggered-when-the-elements-below-cursor-at-mousedown-and-mouseup-events-are-different
EDIT 3:
In addition to Internet Explorer, Google Chrome recently started to have the same behaviour: https://code.google.com/p/chromium/issues/detail?id=484655
I bumped into this issue too. I decided I'd make a jQuery plugin to solve this issue and put it on GitHub.
It's all here, feedback is welcome : https://github.com/louisameline/XClick
#mkurz : thanks for finding that W3 directive, you saved me a lot of time.
#vitalets : I solved this issue because I use select2 like you (you led me to this topic). I'll fork the select2 repo and leave a message for the people interested in this.
I'll see if I can ask the Microsoft people to take a look at it and hopefully change that annoying click behavior.
I also struggled with such behavior. I've modified your fiddle to find out how all mouse events are triggered with dynamically created overlay:
http://jsfiddle.net/Fh4sK/9/
So, when mousedown handler of some element shows overlay on the same place where mousedown occured:
Chrome, FF:
mousedown triggered on element
mouseup triggered on overlay
click does not trigger
IE:
mousedown triggered on element
mouseup triggered on overlay
click triggered on BODY(!)
The same behavior if you hide mask in mousedown.
This issue can lead to weird things in IE with modals, masks, overlays etc..
Intresting thing: if you show overlay in mouseup instead of mousedown - everything works
The solution I found is to use mouseup instead of mousedown.
It can't be explained normally because both these events are triggered before click.
In that case all mouse events are triggered on element:
http://jsfiddle.net/Fh4sK/11/
mousedown triggered on element
mouseup triggered on element
click triggered on element
Hope this helps.
You could filter the document click by the target to exclude clicks on that div:
$(document).on('click', function(event) {
if(!$(event.target).hasClass('clickme')) {
console.log('body click');
}
});
If you want to stop bubbling of the click event, try this : (I don't have IE to test)
$(document).on('click', function(event) {
console.log('body click');
});
$('.clickme').on('mousedown', function(event) {
console.log('div mousedown');
});
$('.clickme').on('click', function(event) {
event.stopPropagation();
});
Click, mousedown and mouseup are differents events, independant.
here is a similar question

Webkit <button onclick> does not fire in certain situations

I noticed that in Webkit the <button> element does not fire the onclick event when the mouse is moved from/to child elements of the button during the click. With other words: when the mousedown and mouseup events do not happen on the same element - even if both are children of the button.
The same happens when clicking/releasing on/out of the pixels of the button text.
To clarify I made a testcase: http://jsfiddle.net/gx9B3/
It works fine in FireFox. Fails in Chrome 15 and QtWebkit 4.7.1
Is there a way around this? I need a solution specifically for Webkit because my project is targeted to this browser only.
Solution
I could solve this problem based on the method suggested by Jan Kuča (the solution I accepted). Some additional tweaks were necessary, especially introducing a timer to avoid double clicks. Have a look at my fully working solution at JSFiddle:
http://jsfiddle.net/mwFQq/
You could set up a mousedown listener on document.body (to fix the problem on the whole page). You would check if the mousedown event originated from an HTMLButtonElement (or from any of its child elements) and if it did, you set up a mouseup listener (on the button so it does not have to bubble too much) that will check the target property of the mouseup event. If it is contained in the button and is different from the target of the mousedown event, you fire a click event like this:
var e = document.createEvent('Events');
e.initEvent('click', true, true);
button.dispatchEvent(e);
(Do this only for WebKit-based browsers so that you don't get multiple click events in other browsers. Or you could call the preventDefault method of the mousedown event as it should also prevent firing the click event.)
You could try adding this CSS style:
button * {
pointer-events: none;
}
Child elements will then ignore mouse events and the click will come from the button element itself. Here's an example http://jsfiddle.net/Tetaxa/gx9B3/2/
You can also solve it by pure CSS trick. Just place pseudo element over <button> to cover text:
button {
position:relative;
}
button:after {
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
content:'';
}

Javascript: Multiple mouseout events triggered

I'm aware of the different event models in Javascript (the WC3 model versus the Microsoft model), as well as the difference between bubbling and capturing. However, after a few hours reading various articles about this issue, I'm still unsure how to properly code the following seemingly simple behavior:
If I have an outer div and an inner div element, I want a single mouse-out event to be triggered when the mouse leaves the outer-div. When the mouse crosses from the inner-div to the outer-div, nothing should happen, and when the mouse crosses from the outer-div to the inner-div nothing should happen. The event should only fire if the mouse moves from the outer-div to the surrounding page.
<div id="outer" style = "width:20em; height:20em; border:1px solid #F00" align = "center" onmouseout="alert('mouseout event!')" >
<div id="inner" style = "width:18em; height:18em; border:1px solid #000"></div>
</div>
Now, if I place the "mouseout" event on the outer-div, two mouse-out events are fired when the mouse moves from the inner-div to the surrounding page, because the event fires once when the mouse moves from inner to outer, and then again when it moves from outer to the surrounding page.
I know I can cancel the event using ev.stopPropagation(), so I tried registering an event handler with the inner-div to cancel the event propagation. However, this won't prevent the event from firing when the mouse moves from the outer-div to the inner-div.
So, unless I'm overlooking something, it seems to me this behavior can't be accomplished without complex mouse-tracking functions. In the future, I plan to reimplement a lot of this code using a more advanced framework, like JQuery, but for now, I'm wondering if there is a simple way to implement the above behavior in regular Javascript.
The mouseout event on the inner div ‘bubbles’ to the outer div. To detect that this has happened from the outer div, check the target property of the event:
<div id="outer">
<div id="inner">x</div>
</div>
document.getElementById('outer').onmouseout= function(event) {
// deal with IE nonsense
if (event===undefined) event= window.event;
var target= 'target' in event? event.target : event.srcElement;
if (target!==this) return;
...
};
The usual problem with mouseout is you get it when the pointer moves “out” of the parent even if it's only moving “in” to the child. You can detect this case manually by looking up the ancestor list of the element the mouse is moving into:
var other= 'relatedTarget' in event? event.relatedTarget : event.toElement;
while ((other= other.parentNode).nodeType===1)
if (other===this) return;
This is the mousein/mouseout model: it is only interested about which element is the mouse's immediate parent. What you more often want is the mouseenter/mouseleave model, which considers element trees as a whole, so you'd only get mouseleave when the pointer was leaving the element-or-its-descendants and not moving directly into the element-or-its-descendants.
Unfortunately mouseenter/mouseleave is currently an IE-only event pair. Hopefully other browsers will pick it up as it is expected to be part of DOM Level 3 Events.

Categories