Are simultaneous mouseover and mousemove events possible?

J

JB

I am struggling to figure out a way to allow one element
to be dragged, but still capture 'mouseover' events on
other elements.

I've created a simple example to demonstrate what I mean:
http://gerromorpha.cs.uoregon.edu/moveAndOver.html
(The above link is only meant for Windows/Firefox.)

It's not much code, but it's probably too much to paste here.
The gist of it is, when a user clicks on element #1, I attach
the mousemove event to element #1. That event remains
attached until the user un-clicks (mouseup). During the time
the user is dragging element #1, however, element #2 doesn't
register the mouseover events it should.

Any ideas on how to get around this? If I should be approaching
this in a completely different way I would appreciate some
alternatives. Any response, for that matter, will be greatly
appreciated.
 
M

Martyr2

You know, I was working on this very thing a couple weeks ago to
implement classic drag and drop. Luckily for you I have explored this
issue extensively and can save you hours of researching by telling you
what I found out.

The short of it all, you can't. Both IE and FF just don't allow it
right now. What you do need is a collusion function which will tell you
when one item (the item you are dragging) has collided (is over)
another element. When the two elements collide, you can call a function
which will give you the same effect as mouseover on the second element.

I got this off a guy here on the forums, and even though it is a bit
unreadable, it works like a charm and allows me to do the drag and drop
I need.

hitTest = function(o, l){
function getOffset(o){
for(var r = {l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth,
b: o.offsetHeight};
o = o.offsetParent; r.l += o.offsetLeft, r.t +=
o.offsetTop);


return r.r += r.l, r.b += r.t, r;
}
for(var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j
? l = [l] : l).length; i;
b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r :
b.l <= a.r))
&& (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) &&
(r[r.length] = l));
return j ? !!r.length : r;
};

Use: hitTest(obj1,obj2)
Description: Returns true and false if the reference to obj1 and obj2
overlap or "collided".

You would put this in your mousemove event within an if statement. If
it is true, call the function you want to call as if it was in a
mouseover of the second element.

Good luck! :)
 
V

VK

JB said:
I am struggling to figure out a way to allow one element
to be dragged, but still capture 'mouseover' events on
other elements.

I've created a simple example to demonstrate what I mean:
http://gerromorpha.cs.uoregon.edu/moveAndOver.html
(The above link is only meant for Windows/Firefox.)

In addition to the previous answer:

It should be clear that the problem is *not* that mouse events are not
reaching div2. Events are going Russian hills (down to bottom and up to
high) in Firefox and Bubble way (from the bottom to high) in IE, and
nothing is changed while dragging. As your current drag script is
vulnerable to the "drag off the body" trick, you can see it yourself
(and would be nice to fix it in the release of course). Start dragging
div1 to the menu bar until it stops. Release button an return mouse to
the screen. div1 is still in dragging mode but doesn't move (you may
need to repeat this by changing the return path for the mouse to get
this). Now if you move your mouse over div2, the mouseover event will
be properly captured.

So the problem is not in event capturing, but in the fact that while
dragging the mouse is not over div2 - it is over div1. div1 is over
div2 - that's true, but there is no handler like onDiv1Over to capture
:)

So you need to use coords rectangle check as suggested for FF and
others. For IE it is enough to use the native
document.elementFromPoint(x,y) method.
 
J

JB

Wow. That is some serious abuse of for-loops.
It does seem to mostly work. I have translated
it into more readable code and I will post it here
for future reference.

I am not sure I understand exactly what's going on,
but I think if obj2 (in my translated code) is a single
object, it's converted into a single-element array.
Otherwise, if the user passed an array of objects,
hitTest() detect collisions with all the elements in
that array. I have tried sending the function an
array of objects but I couldn't get it to work.

hitTest = function(obj1, obj2){
function getOffset(obj){
var objBounds = {
objLeft: obj.offsetLeft,
objTop: obj.offsetTop,
objRight: obj.offsetWidth,
objBottom: obj.offsetHeight
};

for( ; obj = obj.offsetParent; )
{
objBounds.objLeft += obj.offsetLeft;
objBounds.objTop += obj.offsetTop;
}

objBounds.objRight += objBounds.objLeft
objBounds.objBottom += objBounds.objTop

return objBounds;
}

var obj2Bounds
var collisionArray = [];
var obj1Bounds = getOffset(obj1);

// My guess:
// If obj2 is a single object, convert it to a single
// element array. Now that obj2 is definitely an
// array, set i to the length of that array
var j = isNaN(obj2.length);
var i = (j ? obj2 = [obj2] : obj2).length

for(; i; )
{
obj2Bounds = getOffset(obj2[--i])

if ( (obj1Bounds.objLeft == obj2Bounds.objLeft ||
(obj1Bounds.objLeft > obj2Bounds.objLeft ?
obj1Bounds.objLeft <= obj2Bounds.objRight :
obj2Bounds.objLeft <= obj1Bounds.objRight))
&& (obj1Bounds.objTop == obj2Bounds.objTop ||
(obj1Bounds.objTop > obj2Bounds.objTop ?
obj1Bounds.objTop <= obj2Bounds.objBottom :
obj2Bounds.objTop <= obj1Bounds.objBottom)))
{
collisionArray[collisionArray.length] = obj2;
}
}

// My guess:
// if obj2 is a single object and it collides with obj1,
// collisionArray.length == 1 so !!collisionArray.length == true
// if obj2 is an array of objects, return an array of
// the subset of obj2 objects with which obj1 collidess
return j ? !!collisionArray.length : collisionArray;
};


Thanks for your post, you saved me a lot of pain.


You know, I was working on this very thing a couple weeks ago to
implement classic drag and drop. Luckily for you I have explored this
issue extensively and can save you hours of researching by telling you
what I found out.

The short of it all, you can't. Both IE and FF just don't allow it
right now. What you do need is a collusion function which will tell you
when one item (the item you are dragging) has collided (is over)
another element. When the two elements collide, you can call a function
which will give you the same effect as mouseover on the second element.

I got this off a guy here on the forums, and even though it is a bit
unreadable, it works like a charm and allows me to do the drag and drop
I need.

hitTest = function(o, l){
function getOffset(o){
for(var r = {l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth,
b: o.offsetHeight};
o = o.offsetParent; r.l += o.offsetLeft, r.t +=
o.offsetTop);


return r.r += r.l, r.b += r.t, r;
}
for(var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j
? l = [l] : l).length; i;
b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r :
b.l <= a.r))
&& (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) &&
(r[r.length] = l));
return j ? !!r.length : r;
};

Use: hitTest(obj1,obj2)
Description: Returns true and false if the reference to obj1 and obj2
overlap or "collided".

You would put this in your mousemove event within an if statement. If
it is true, call the function you want to call as if it was in a
mouseover of the second element.

Good luck! :)
 
J

JB

So the problem is not in event capturing, but in the fact that while
dragging the mouse is not over div2 - it is over div1. div1 is over
div2 - that's true, but there is no handler like onDiv1Over to capture

That makes perfect sense. I feel stupid for not thinking of that.

Thanks for your reply.
 
R

RobG

JB said on 20/03/2006 7:24 AM AEST:
Wow. That is some serious abuse of for-loops.
It does seem to mostly work. I have translated
it into more readable code and I will post it here
for future reference.

I've done a similar thing by creating a object that contains 'drop
zones'. As you drag one element, you see if it's over a drop zone. By
storing the co-ords of each drop-zone (and updating them if/when they
move) the algorithm for seeing if the dragged element is 'over' a drop
zone is much faster than finding the co-ords from scratch every time.

If you have a lot of drop zones, give them a key based on their
co-ordinates. Then keep the keys in an array and use either a btree or
quadtree search to find 'hits'. That will allow you to keep track of
hundreds of drop zones without too much processing overhead.


[...]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top