I have a <div> element that is created in my script and appended to another <div>. I have:
coverElm.onmousedown = mouseDownEventHandeler;
document.onmouseup = mouseUpEventHandeler;
document.onmousemove = mouseMoveEventHandeler;
I have the functions defined and work great and keep track of if the mouse is down with a boolean mouseDown.
The Problem - When the mouse is pressed down and is released the document.onmouseup is never handled. I think its because its doing a drag of whatever is in the <div> witch is just a few words of text. I have this issue without text too.
So what I'm looking for is a way to prevent this odd dragging behavior, or way for onmousedrag to see if the mouse is pressed down of not - NOT USING THE MOUSE UP AND MOUSE DOWN METHODS
Here are my functions:
function mouseUpEventHandeler(e) {
mouseDown = false;
}
function mouseDownEventHandeler(e) {
mouseDown = true;
}
function mouseMoveEventHandeler(e) {
if (mouseDown) {
coverElm.innerHTML ="<p>Mouse down and dragging</p>";
}
}
90% of the time, this is because the dragged element is in front of the element with the mouseup event listener, so the parent element underneath never gets the event.
Usually, this can be fixed by using addEventListener as opposed to the inline form of the event. Another way to fix this is to give the dragged element an eventListener for when the mouse is released. Also, you can have a div that is put in front of all other elements whenever an element starts to drag (via the zIndex property).
Edit: I whipped up some proof-of-concept code:
http://jsfiddle.net/2BkEM/5/
$j(divID).bind('dragstart', function (event) { event.preventDefault() });
Thanks all
Related
I am working on a video player. This player is used inside an iframe on the client's page, so it is cross-origin. When the user clicks on seekbar and drags the cursor out of iframe, I can't identify the mouseup event, so it keeps selected. I notice that youtube player can do it, and can identify cursor events outside iframe. How can i do it using javascript?
One thing to consider is that the mouseup event only fires if the pointer is directly over the element with the eventListener. It doesn't keep track of what element the mousedown event fired on, it only knows about the state of the mouse at the time of the mouseup.
The mouseup event is fired at an Element when a button on a pointing
device (such as a mouse or trackpad) is released while the pointer is
located inside it.
https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event
Therefore you need some other method of tracking if your user has initiated the drag on your seekbar playhead.
A possible solution is to use a boolean variable. Then to listen to mouseup on document and check the value of that variable.
let scrubbingTimeline = false;
document.querySelector('button').addEventListener('mousedown', ()=> {
console.log('Mouse down')
scrubbingTimeline = true
})
document.addEventListener('mouseup', ()=> {
if(scrubbingTimeline === true) {
console.log('Mouse has been released')
scrubbingTimeline = false
}
})
Another possible solution would be to initiate the mouseup listener only once the mousedown callback has fired. Then to stop listening after one event. Like this:
function mouseUpHandler() {
console.log("Mouse Lifted")
document.removeEventListener('mouseup', mouseUpHandler)
}
document.querySelector('button').addEventListener('mousedown', ()=> {
console.log('Down')
document.addEventListener('mouseup', mouseUpHandler)
})
Important: This code is for the inner document. I.E. the one that will be the iframe.
I can get mouse motion events in javascript using a simple function:
myElement.onmousemove(function(event){
// ...
});
but I think those events only fire when the cursor is hovering over the element. I know I can also do this for document and get all mouse motion events:
document.onmousemove(function(event){...});
However, this event doesn't have the "scope" of belonging to the div or button or whatever, and won't have an offset relative to the element I'm working with.
My workaround is to save a variable pointing at the focused div and reference it when getting all motion events, but I'd much rather get motion events and the onmousemove function set for the element itself. Is there a way for that element to continue receiving mouse move events even if the cursor leaves the boundary of the element?
In case it matters I can use jQuery but I would prefer a native solution.
no, it is not possible to get a mousemove event when the movement is outside of the attached element. As you stated correctly, you can track the movement on the document. When using that together with mouseenter and mouseleave events, you can set/unset a variable to indicate if the movements are happening on the element or not.
e.g.
var myElement = document.getElementById("special");
var isOverMyElement = false;
document.addEventListener('mousemove', function(e){
if (isOverMyElement){
console.log("mouse is within myelement at details", e);
}
})
myElement.addEventListener('mousenter', function(e){
isOverMyElement = true;
})
myElement.addEventListener('mousenter', function(e){
isOverMyElement = false;
})
If you are targeting older browsers, use jQuery as it normalizes mouseenter/leave over browsers. If you are targeting modern browsers, there is no need for jQuery
I have this little code. It's the beginnig of a drawing app: https://jsfiddle.net/t2xycjcd/1/ (building on code by www.williammalone.com)
This is the key listener:
canvas.onkeypress = function (e) {
e.preventDefault();
var letter = String.fromCharCode(e.keyCode);
if (keyMap[letter]) {
settings[keyMap[letter].param] = keyMap[letter].value;
refreshToolStatusDisplay();
redraw();
}
}
This is the mouse listener (nothing fancy):
canvas.onmousedown = function(e)
{
console.log("mousedown");
clickX = offsetX(e, canvas);
...
I am getting crazy about mouse events not correctly firing when happening short time (or immediately) after a key event.
How to reproduce:
use the line tool to draw a line
press the "p" key to select "player" tool
now without pausing and without moving the mouse -> click
The mousedown event will not fire.
It will however if you either wait about one second before step three, or move the mouse or click one more time.
This is kind of catastrophic for my purpose.
I have tried various things but none worked:
Resetting the focus to the canvas element after the key event -> not the problem, the canvas still has the focus
Firing a custom mouse event after the key event through a MouseEvent.initMouseEvent() -> not working
Other crazy stuff not worth mentioning
This seems to be typical behaviour of canvas elements. This is a
similar but unrelated fiddle of a canvas element with a mouse listener: http://jsfiddle.net/4Lanc/ -> Same behaviour there: clicking shortly after a key press will prevent the click from happening.
Anybody have an idea?
I have some javascript which allows elements to be dragged from one div to another. I want to write some code which attaches the element to the mouse cursor during the drag, so the user can see it move places.
I have tried two things which dont quite work. Any advice on how i can tweak my code?
Attempt one
Change positioning of dragged element to absolute and set top,left to the mouse coordinates.
The problem with this is that it only fires on mouseup.. the element has an ondragstart listener. When i drag the element nothing happens; only when it gets dropped, it attaches to the mouse.
function drag(ev)
{
ev.dataTransfer.setData("Text",ev.target.id);
var data=ev.dataTransfer.getData("Text");
body = document.getElementById('body');
mouse = captureMouse(body);
body.addEventListener('mousemove',function(event)
{
document.getElementById(data).style.position="absolute";
document.getElementById(data).style.top=mouse.y+"px";
document.getElementById(data).style.left=mouse.x+"px";
},false);
}
captureMouse = function(element)
{
var mouse = {x:100, y:100}
element.addEventListener('mousemove',function(event)
{
var x,y;
if(event.pageX||event.pageY)
{
x=event.pageX;
y=event.pageY;
}else{
x=event.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;
y=event.clientX+document.body.scrollTop+document.documentElement.scrollTop
}
x-=element.offsetLeft;
y-=element.offsetTop;
mouse.x=x;
mouse.y=y;
},false);
return mouse;
}
attempt two
This uses the same capture mouse function as above. This time, in addition to the ondragstart listener assigned to the element in the HTML, i also put an onmousedown listener which fires the below. The problem here is that it clashes with the ondragstart. When i give the target element an absolute position, the inline-block element next to it takes its place and is picked up by ondragstart instead.
Any suggestions? perhaps i should get rid of my ondragstart ondragover etc completely and replace with
onmousedown and onmouseup listeners?
function absolute(x){
body = document.getElementById('body');
mouse = captureMouse(body);
body.addEventListener('mousemove',function(event){
document.getElementById(x).style.position="absolute";
document.getElementById(x).style.top=mouse.y+"px";
document.getElementById(x).style.left=mouse.x+"px";
},false);
}
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