HTML Javascript Drag and Drop: Onmousedown or ondragstart - javascript

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

Related

Does onMouseOut event occurs when I remove in javascript the element the mouse is currently over?

I have an image.
I attached onclick listener and onmouseout listener to it.
onclick listener does parent.removeChild(this) operation.
Image disappears. Nothing happens until I move my mouse. Then fires onmouseout handler.
Is it even possible?
And how, I assume not by propagation because there is nothing attached to onmouseout on parent elements.
Handlers:
function ballMouseoutHandler(e) {
event.stopPropagation();
this.src = ballSrc;
alert('mouse out!');
}
function ballMouseoverHandler() {
this.src = ballInvSrc;
}
function ballClickHandler() {
gameBalls.removeChild(this);
}
Code adding image:
gameBalls = document.getElementById('balls-col');
var tmpBall = createBall();
gameBalls.appendChild(tmpBall);
tmpBall.addEventListener('click', ballClickHandler);
tmpBall.addEventListener('mouseover', ballMouseoverHandler);
tmpBall.addEventListener('mouseout', function(e) {ballMouseoutHandler(e);});
function createBall() {
var ball = new Image();
ball.src = ballSrc;
ball.classList.add('ball');
return ball;
}
It is a part of larger code. So what I have done so far is that I extracted the particular problem to separate smaller files. And it still looks as though the mouse out event was triggered.
OK. After some testing I can say that yes - removing an DOM Object from under the mouse pointer triggers the onmouseout event. Which if you think is quite logic because the move is relative and what counts is that mouse pointer was over the object and now it is out. :)
So what I had to do was to remove Event Listener before removing the Object.

How to get the mouse position from 'drag' events in Firefox

With "draggable" domNoes, the 'drag' event doesn't seem to contain any mouse information at all (like offsetX and offsetY) like it does in chrome. How can I get this information while dragging?
I've tried setting a 'mousemove' event handler on the document, but it seems like that isn't fired when something is being dragged. Same seems to be true in chrome. What a drag..
I'm using Firefox 30.
drag events are used on the elements that are being dragged. dragover can be used on other elements that stay stationary such that something is dragged over them. If you ask for clientX or clientY on drag events, you will get zeros. If you drag that element over an element with dragover, and ask that event for coordinates, you will get x and y relative to the dragover listening element.
Easiest example to see it in action is to apply dragover to the document object, then drag something over it and print the x and y.
Update
Sorry for the delayed response, I've been very busy. If you have the mousemove event attached to the actual document rather than the element, then it should still fire. For example:
document.addEventListener("mousemove", moveHandler, true);
Notice that I set the boolean at the end to "true"; this means that the event fires in the capture phase. It should update the coordinates in your mouse move function as you're needing. As for getting the element being dragged, you can get that element reference from event.target in the drag handler, like this:
function dragHandler(e) {
var el = e.target; //this is the element being dragged
}
You can use this to ascertain the offset of the element that you're dragging:
var rectObject = element.getBoundingClientRect();
var top_offset = rectObject.top;
var left_offset = rectObject.left;
If you need mouse coordinates, you'll need to attach a mousemove event listener to the DOM and store the coordinates in a global variable so that you can access them in the drag listener.

Javascript - Double File Dragover Event Firing

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

JavaScript mouseUp event ignored when mouse is released during drag

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

Check if another event is fired from an event handler function

Let's imagine I have an event handler function attached to an image, for example, an onmouseover handler. I don't want this handler run if an onmouseover event is fired by a particular element.
To be more specific, I have an image which being hovered, a menu is popped out. I want to close that menu if the mouse moves out of that image, unless I move the mouse to the menu, which is adjacent to the image.
So something like this in pseudo code:
img.mouseout = function () {
if (otherelement.onmouseover.fired) {
leave the menu as it is
}
else
{
close the menu
}
So how can I check whether another event was fired?
I would add a timeout and a mouseover handler that clears it in the other handler. While the elements could be exactly next to each other visually, adding a small timer is more safe since there might just be a tiny 1px area where the mouseout event fires, causing an ugly flickering.
This also allows a quick mouseshake without flicker.
Something like:
var timer;
img.onmouseout = other.onmouseout = function() {
timer = setTimeout(closemenu, 100);
}
img.onmouseover = other.onmouseover = function() {
clearTimeout(timer);
}
function closemenu() {
// close it
}

Categories

Resources