drag and drop target strategies

P

Peter Michaux

Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation.

1) When a drag is in progress, use the native mouseover to know when
the mouse cursor enters a drop target element. In order for the native
mouseover event to fire, there cannot be either the dragged item or a
proxy of the dragged item moving around and under with the cursor. If
there is an item like this with high z-index between the cursor and
target, then the mouseover event for the target cannot fire because
the cursor is always over the dragged item. There can be a dragged
item beside the cursor but that isn't very visually pleasing or what
the user is accustomed to experiencing. This technique is not so great
when dragging in a list. If the cursor is between two <li> elements
then it is over the <ul> and so where to show the item will drop? Also
deciding if the item will drop above or below a particular <li> or
will drop at the end of the list is a bit messy.

2) If the dragged item must look like it is remaining under the cursor
during the drag, then there is another strategy I saw a long time ago.
Extra transparent elements are created and added to the page directly
above (i.e. large z-index) the target zones. If these transparent
elements have z-index of 10, then the dragged item has a z-index of 5.
That way the dragged item appears to be under the cursor but the
mouseover events for the extra elements still fire as there is nothing
between the cursor and the extra elements. This is appealing but the
setup is expensive adding elements to the page. If the page is
scrolling or has scrolling elements the exact placement of the extra
elements is tricky as position reporting of the real target elements
in the page can be tricky.

3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.

I don't think I've seen any other way of doing things that might be
worth considering. The main concerns are the appearance of the dragged
item (under or beside the mouse cursor) and the speed of showing the
current drop target for each mousemove. Since the mousemove event
fires in quick succession the computation needs to be fast.

I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.

I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.

Thanks,
Peter
 
J

jeff

Peter said:
Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation.
3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets.

I've had no need for drag and drop on an item, but I have done
something very much like option 3 for other uses.

On another note, Scriptaculous has resortable lists,

http://wiki.github.com/madrobby/scriptaculous/sortable-lists-demo

you can drag an item of the list around and it then snaps to the nearest
list position. I have no idea how they do that and never had enough
desire to look through the code, which is no joy.

Under no circumstance do I recommend using Scripataculous. But I do
find that bit clever.

When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above.


I suppose you could always recalculate element positions if the page
changes (potentially hard to know), or just periodically re-poll (which
I've done), but perhaps it is only necessary to know which target you
are closest to?

Jeff
 
P

Peter Michaux

Peter Michaux wrote:


   I've had no need for drag and drop on an item, but I have done
something very much like option 3 for other uses.

   On another note, Scriptaculous has resortable lists,

http://wiki.github.com/madrobby/scriptaculous/sortable-lists-demo

you can drag an item of the list around and it then snaps to the nearest
list position. I have no idea how they do that and never had enough
desire to look through the code, which is no joy.

  Under no circumstance do I recommend using Scripataculous. But I do
find that bit clever.

About three years ago, I read the Scriptaculous drag and drop code and
the sortable list code. It was hideous to read and ran very slowly.
With only a moderately long list the mousemove behavior became very
slow as the computation was very complex. That is when I started
looking more seriously into other options. Perhaps the Scriptaculous
code has improved by now.

Peter
 
R

RobG

Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation. [...]
3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.

Have you considered a quadtree[1]? I've never programmed one, but
they are commonly used in CAD and other graphics applications dealing
with 2d and 3d spatial indexing.

The downside is that calculating the index can be slow, but as long as
the elements don't move around too much, it shouldn't be an issue. It
may not be suitable for the small number of tragets in a typical HTML
page.

There are optimised forms that might be better, such as R-Tree[2].

1. <URL: http://en.wikipedia.org/wiki/Quadtree >
<URL:
http://books.google.com.au/books?id...resnum=1&ved=0CAkQ6AEwAA#v=onepage&q=&f=false
2. <URL: http://en.wikipedia.org/wiki/R-tree >
 
P

Peter Michaux

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation. [...]
3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.

Have you considered a quadtree[1]?  I've never programmed one, but
they are commonly used in CAD and other graphics applications dealing
with 2d and 3d spatial indexing.

No I hadn't considered a quadtree but I can imagine it would be useful
to keep the number of regions low. Perhaps it would be especially
useful if there were circular shaped targets.

Thanks for the links.

Peter
 
R

Richard Cornford

On Feb 16, 1:20 am, Peter Michaux wrote:
I'm curious which of the above methods people here have used
and if there are any other techniques worth considering.

I once proposed here (and not entirely seriously) a set up where the
thing that was dragged got represented by 4 clipped copies of itself,
tiled together to re-create the impression of most of the original
object, but with a hole in the middle over which the mouse pointer sat
and through which it could generate mouseover/out events in the
underlying DOM. Something like (best viewed with a fixed-width font):-

+----------------------+
| |
| |
+----------------------+
+-------+ +--------+
| | | |
| | | |
| | | |
+-------+ +--------+
+----------------------+
| |
| |
+----------------------+

Thus you have the impression that the thing being dragged is under the
mouse without its actually blocking the mouse.

I never tried it, and some obvious potential issues should already be
occurring to anyone reading this.

Richard.
 
P

Peter Michaux

On Feb 16, 1:20 am, Peter Michaux wrote:


I once proposed here (and not entirely seriously) a set up where the
thing that was dragged got represented by 4 clipped copies of itself,
tiled together to re-create the impression of most of the original
object, but with a hole in the middle over which the mouse pointer sat
and through which it could generate mouseover/out events in the
underlying DOM. Something like (best viewed with a fixed-width font):-

+----------------------+
|                      |
|                      |
+----------------------+
+-------+     +--------+
|       |     |        |
|       |     |        |
|       |     |        |
+-------+     +--------+
+----------------------+
|                      |
|                      |
+----------------------+

Thus you have the impression that the thing being dragged is under the
mouse without its actually blocking the mouse.

I never tried it, and some obvious potential issues should already be
occurring to anyone reading this.

I had the same idea and did try it. I wrote about the idea with an
example page:

http://peter.michaux.ca/articles/new-dragdrop-recipe-donut-dragdrop

The hole size is important. If it is too small then the hole cannot
keep up and the mouse ends up over one of the proxy pieces which
causes problems.

It works well but for some reason I find the four-piece proxy image
very unappealing implementationally. Lining up the bits in the four
pieces can be difficult depending on the contents.

That said, I have used this technique in production once and it has
happily been in production for close to four years without any
problems.

This technique is essentially the option 1 that I listed in my
original post of this thread. It has the same problems: If the real
targets do not 100% cover the user-perceived drop zones then it is
difficult to know what a drop should do.

Thanks,
Peter
 
T

Thomas 'PointedEars' Lahn

Peter said:
[...]
I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.

I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.

Sounds like going at lengths to invent a polygonous wheel without properly
testing the other inventor's round one before, which strikes me as being a
particularly stupid idea.


PointedEars
 
P

Peter Michaux

Peter said:
[...]
I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.
I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.

Sounds like going at lengths to invent a polygonous wheel without properly
testing the other inventor's round one before, which strikes me as being a
particularly stupid idea.

Can you confirm that native drag and drop is, in fact, a round wheel
and can provide cross-browser functionality?

Peter
 
T

Thomas 'PointedEars' Lahn

Peter said:
Thomas said:
Peter said:
[...]
I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.
I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.

Sounds like going at lengths to invent a polygonous wheel without
properly testing the other inventor's round one before, which strikes me
as being a particularly stupid idea.

Can you confirm that native drag and drop is, in fact, a round wheel
and can provide cross-browser functionality?

Perhaps, but that task would be up to you.


PointedEars
 
P

Peter Michaux

Peter said:
Thomas said:
Peter Michaux wrote:
[...]
I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.
I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.
Sounds like going at lengths to invent a polygonous wheel without
properly testing the other inventor's round one before, which strikes me
as being a particularly stupid idea.
Can you confirm that native drag and drop is, in fact, a round wheel
and can provide cross-browser functionality?

Perhaps, but that task would be up to you.

I'm not asking you to do any work but to state whether you already
know that the wheel is round or not. You say that the other inventor's
wheel is round implying you already know it is and that I simply need
to test it to see that is the case. From what I've seen that other
inventor's wheel is just a different polygon than the DHTML drag and
drop solution wheels.

I have not seen a way to control the appearance of the drag proxy for
native drag and drop in IE, for example. If that cannot be done then
that is game over for even considering native drag and drop in IE for
any drag and drop purpose that I've encountered.

Peter
 
T

Thomas 'PointedEars' Lahn

Peter said:
Thomas said:
Peter said:
Thomas 'PointedEars' Lahn wrote:
Peter Michaux wrote:
[...]
I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.

I'm curious which of the above methods people here have used and if
there are any other techniques worth considering.
Sounds like going at lengths to invent a polygonous wheel without
properly testing the other inventor's round one before, which strikes
me as being a particularly stupid idea.
Can you confirm that native drag and drop is, in fact, a round wheel
and can provide cross-browser functionality?
Perhaps, but that task would be up to you.

I'm not asking you to do any work but to state whether you already
know that the wheel is round or not. You say that the other inventor's
wheel is round implying you already know it is and that I simply need to
test it to see that is the case.

No, that is a fallacy. I am merely saying that "little investigation" does
not provide sufficient a reason for going at lengths to reinvent the wheel.
From what I've seen that other inventor's wheel is just a different
polygon than the DHTML drag and drop solution wheels.

You had not stated your reasons by then.
I have not seen a way to control the appearance of the drag proxy for
native drag and drop in IE, for example. If that cannot be done then
that is game over for even considering native drag and drop in IE for
any drag and drop purpose that I've encountered.

Define: drag proxy. Besides, your logic is flawed.


PointedEars
 
R

RobG

Hi,
One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation. [...]
3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.
Have you considered a quadtree[1]? I've never programmed one, but
they are commonly used in CAD and other graphics applications dealing
with 2d and 3d spatial indexing.

No I hadn't considered a quadtree but I can imagine it would be useful
to keep the number of regions low. Perhaps it would be especially
useful if there were circular shaped targets.

The shapes of the targets isn't relevant. The idea is to create an
quadtree index to the target shapes. The cursor position is converted
to a spatial key and if there are no targets within the region, no
further search is required.

If there are targets in the region the cursor is over, search to see
if it's over any of those particular targets. When the quadtree is
created, the position of the targets is stored so determining which
ones the cursor is over is pretty quick. If the targets don't overlap,
life is easier - as long as the cursor is over a target, no search for
other targets is necessary.

The down side comes if the targets change their position in the page,
the index and stored positions of targets need to be recalculated. But
there should be a bit of slack after a move is complete and before the
next one starts so it might be unnoticeable. There are also very
efficient methods to do the re-calculation using shifts rather than
working out a new position and index for every target from scratch
every time.

There may be better strategies to deal with a large number of targets
in a small region of a page, e.g. when re-ordering a list.
 
D

dhtml

Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation.

1) When a drag is in progress, use the native mouseover to know when
the mouse cursor enters a drop target element. In order for the native
mouseover event to fire, there cannot be either the dragged item or a
proxy of the dragged item moving around and under with the cursor. If
there is an item like this with high z-index between the cursor and
target, then the mouseover event for the target cannot fire because
the cursor is always over the dragged item. There can be a dragged
item beside the cursor but that isn't very visually pleasing or what
the user is accustomed to experiencing. This technique is not so great
when dragging in a list. If the cursor is between two <li> elements
then it is over the <ul> and so where to show the item will drop? Also
deciding if the item will drop above or below a particular <li> or
will drop at the end of the list is a bit messy.

2) If the dragged item must look like it is remaining under the cursor
during the drag, then there is another strategy I saw a long time ago.
Extra transparent elements are created and added to the page directly
above (i.e. large z-index) the target zones. If these transparent
elements have z-index of 10, then the dragged item has a z-index of 5.
That way the dragged item appears to be under the cursor but the
mouseover events for the extra elements still fire as there is nothing
between the cursor and the extra elements. This is appealing but the
setup is expensive adding elements to the page. If the page is
scrolling or has scrolling elements the exact placement of the extra
elements is tricky as position reporting of the real target elements
in the page can be tricky.

3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.

Drop targets that do not have ondragover/ondragout or
dragOverClassName can be excluded from that list.

That leaves open the possiblity for a delegation strategy for generic
list-based sorting, e.g.

function delegateMouseover(ev) {
var target = DragAPI.getTarget(ev);
var isListItem = DragAPI.isListItem(target);
//...
}

If mousemove is slowing down too much, then look at why. Profile the
function calls using Firebug to see what it is doing that is to heavy.
Like other events, mousemove can be throttled. Although some would
prefer using a setTimeout or setInterval on mousedown, and cancelling
that on mouseup, it is actually more efficient to throttle the event
by adding to the first step of mousemove, a check of now against last
mousemove time. e.g.

if(now - lastMouseMoveTime < THRESHOLD) return;

I just moved and not using my own machine. everything's in boxes and
stuff. Still catching up on other posts.

Garrett
 
D

dhtml

[...]

That leaves open the possiblity for a delegation strategy for generic
list-based sorting, e.g.

function delegateMouseover(ev) {
  var target = DragAPI.getTarget(ev);
  var isListItem = DragAPI.isListItem(target);
//...

}
Actually no that doesn't work because the pointer is over the dragged
object.
 
D

dhtml

Any reason why the proxy object needs to be under the pointer?
There are a number of implementations out there that position
it in a distance of 10 to 20 pixels away from the mouse pointer,
and I've never heard anyone complain about that.

If the proxy is 10px away from the cursor, the proxy is overlapping
both the edge of the drop target and the non-drop target area, and the
user then moves the mouse in the direction of the drop target, over
the proxy, in one swift motion, he has actually moved the mouse over
the proxy and not triggered a mouseover on the droptarget. After that
the proxy moves, exposing the actual drop target.

A throttling strategy makes that scenario more likely because the
mousemove handler has a threshold that limits its frequency.

The further the distance between the proxy and the pointer, the less
likely this will occur. It would probably be best to test on a slower
machine with an older browser, such as an old mac with Safari 2.
 
D

David Mark

dhtml said:
dhtml said:
[...]
That leaves open the possiblity for a delegation strategy for generic
list-based sorting, e.g.
function delegateMouseover(ev) {
var target = DragAPI.getTarget(ev);
var isListItem = DragAPI.isListItem(target);
//...
}
Actually no that doesn't work because the pointer is over the dragged
object.
Any reason why the proxy object needs to be under the pointer?
There are a number of implementations out there that position
it in a distance of 10 to 20 pixels away from the mouse pointer,
and I've never heard anyone complain about that.

If the proxy is 10px away from the cursor, the proxy is overlapping
both the edge of the drop target and the non-drop target area, and the
user then moves the mouse in the direction of the drop target, over
the proxy, in one swift motion, he has actually moved the mouse over
the proxy and not triggered a mouseover on the droptarget. After that
the proxy moves, exposing the actual drop target.

I'm admittedly entering the discussion late, but why would you care
about mouseover events on the _target_. Anything relying on those is a
botched design (and doomed to fail).

Drop targets are just predetermined regions of the document. Therefore,
drag and drop is about basic geometry, not event handling. See My
Library's rendition for example.
 
D

David Mark

Peter said:
Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation.

1) When a drag is in progress, use the native mouseover to know when
the mouse cursor enters a drop target element. In order for the native
mouseover event to fire, there cannot be either the dragged item or a
proxy of the dragged item moving around and under with the cursor. If
there is an item like this with high z-index between the cursor and
target, then the mouseover event for the target cannot fire because
the cursor is always over the dragged item. There can be a dragged
item beside the cursor but that isn't very visually pleasing or what
the user is accustomed to experiencing. This technique is not so great
when dragging in a list. If the cursor is between two <li> elements
then it is over the <ul> and so where to show the item will drop? Also
deciding if the item will drop above or below a particular <li> or
will drop at the end of the list is a bit messy.

2) If the dragged item must look like it is remaining under the cursor
during the drag, then there is another strategy I saw a long time ago.
Extra transparent elements are created and added to the page directly
above (i.e. large z-index) the target zones. If these transparent
elements have z-index of 10, then the dragged item has a z-index of 5.
That way the dragged item appears to be under the cursor but the
mouseover events for the extra elements still fire as there is nothing
between the cursor and the extra elements. This is appealing but the
setup is expensive adding elements to the page. If the page is
scrolling or has scrolling elements the exact placement of the extra
elements is tricky as position reporting of the real target elements
in the page can be tricky.

3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor. Depending how the regions are computed this can be O(n)
lookup with every mousemove event or it can be O(1) if the regions are
all uniform in size. This does require knowing the positions of the
drop targets which can be a problem as mentioned in 2 above. I know
this system can be very fast and I've had impressed users that note
how fast it is compared to some other slow drag and drop
implementations.

3 for sure. ;)
I don't think I've seen any other way of doing things that might be
worth considering. The main concerns are the appearance of the dragged
item (under or beside the mouse cursor) and the speed of showing the
current drop target for each mousemove. Since the mousemove event
fires in quick succession the computation needs to be fast.

I've only investigated native drag and drop APIs a little and they
didn't seem very useful in terms of making an appealing user
experience.

They are used for other things (e.g. dragging links from one browser to
another).
 
D

David Mark

Peter said:
Hi,

One of the first browser scripting techniques I worked with years ago
was DHTML drag and drop. I've come across and thought about three main
techniques to determine when a dragged item is over a drop target. All
of the ways have pros and cons depending on the situation.

1) When a drag is in progress, use the native mouseover to know when
the mouse cursor enters a drop target element. In order for the native
mouseover event to fire, there cannot be either the dragged item or a
proxy of the dragged item moving around and under with the cursor. If
there is an item like this with high z-index between the cursor and
target, then the mouseover event for the target cannot fire because
the cursor is always over the dragged item. There can be a dragged
item beside the cursor but that isn't very visually pleasing or what
the user is accustomed to experiencing. This technique is not so great
when dragging in a list. If the cursor is between two <li> elements
then it is over the <ul> and so where to show the item will drop? Also
deciding if the item will drop above or below a particular <li> or
will drop at the end of the list is a bit messy.

2) If the dragged item must look like it is remaining under the cursor
during the drag, then there is another strategy I saw a long time ago.
Extra transparent elements are created and added to the page directly
above (i.e. large z-index) the target zones. If these transparent
elements have z-index of 10, then the dragged item has a z-index of 5.
That way the dragged item appears to be under the cursor but the
mouseover events for the extra elements still fire as there is nothing
between the cursor and the extra elements. This is appealing but the
setup is expensive adding elements to the page. If the page is
scrolling or has scrolling elements the exact placement of the extra
elements is tricky as position reporting of the real target elements
in the page can be tricky.

3) Another option I have used is when the drag starts to compute
regions of the page that are drop targets. When the mousemove event is
fired during the drag, the position of the mouse is compared to the
computed regions to determine which region currently contains the
mouse cursor.

One caveat. It isn't the position of the mouse, but whether the dragged
element overlaps the regions. May not matter in some contexts, of course.
 

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

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top