General JavaScript Drag Code

M

Martin Rinehart

Thanks to much help from this group I've been able to write
generalized drag code. It's here:

http://www.martinrinehart.com/examples/js-drag-code.html

It's light, about 2KB.

It's been tested in Opera on KDE and on Windows: Chrome, Firefox,
Opera and Safari. It doesn't work on Konqueror (which I use, so I'll
get to that) nor MSIE (which is a puzzle I leave for others). If one
of you creates an MSIE-also version, please tell me how. (Shouldn't be
hard - there's not much code here!)

Known bugs: does weird stuff when your dragging scrolls your page.
I've looked into this just enough to know that it's not trivial.

Also this means that I've learned enough to get my window manager out
of Opera-only jail.

Thanks again.
 
H

Henry

Thanks to much help from this group I've been able to write
generalized drag code. It's here:

http://www.martinrinehart.com/examples/js-drag-code.html

It's light, about 2KB.

It's been tested in Opera on KDE and on Windows: Chrome, Firefox,
Opera and Safari. It doesn't work on Konqueror (which I use,
so I'll get to that) nor MSIE (which is a puzzle I leave for
others). If one of you creates an MSIE-also version, please
tell me how. (Shouldn't be hard - there's not much code here!)
<snip>

One (the first I spotted) that your code does not work with IE is this
bit of code:-

| this.penumbra =
| document.createElement( 'table' );
| var td = document.createElement( 'td' );
| var tr = document.createElement( 'tr' );
| tr.appendChild( td );
| this.penumbra.appendChild( tr );

In HTML DOMs (as opposed to XHTML DOMs, and IE is an HTML only
browser) TR elements must be the children of table section elements
(TBODY, THEAD, TFOOT). In mark-up the occurrence of a first <TR>
directly inside an open <TABLE> can imply an opening <TBODT> tag (both
of the opening and closing TBODY tags are optional), and so the
correct (or at least, an acceptable/predictable) DOM structure gets
created from the HTML mark-up.

When building a TABLE using DOM methods it is possible for browsers to
imply the creation of an intervening TBODY from an attempt append/
insert a TR to/in a TABLE (or if TBODY already exists then append the
TR to that one, rather than creating a new TBODY for each attempt to
add a TR to a TABLE), and it is possible for a browser to tolerate an
XHTML style table structure (where TR can be direct children of
TABLE), but IE browsers do none of these and instead do precisely no
more than the applicable standards say they should. You can complain
when a browser does less, or does different form an applicable
standard, but you cannot reasonably expect them to do more.

Thus for IE you need to explicitly create your own TBODY element,
append it to the table and then append the TRs to that. It is best to
do this with all browsers in all types of DOM (both HTML and XHTML) as
the result will then be a known and predictable table structured
rather than one of the many structures that may possibly result from
the error correction (or XHTML interpretation) of the attempt to
append a TR to a TABLE.
 
M

Martin Rinehart

Henry said:
One (the first I spotted) that your code does not work with IE is this
bit of code:-

| this.penumbra =
| document.createElement( 'table' );
| var td = document.createElement( 'td' );
| var tr = document.createElement( 'tr' );
| tr.appendChild( td );
| this.penumbra.appendChild( tr );

In HTML DOMs (as opposed to XHTML DOMs, and IE is an HTML only
browser) TR elements must be the children of table section elements
(TBODY, THEAD, TFOOT).

Thank you, Henry! Everybody else who has to support MSIE should also
thank you. Made that one change and it worked in MSIE. (By far the
most jagged drag, which means we're handling fewer mouse moves, which
means this browser is the slowest. Not surprisingly.)

Those of you who have older browsers: some other tests would be
helpful.
 
H

Henry

Thank you, Henry! Everybody else who has to support MSIE should
also thank you. Made that one change and it worked in MSIE.

That would probably depend a bit on whether they were already familiar
with the issue, or HTML in which case the situation might be expected.
(By far the most jagged drag, which means we're handling
fewer mouse moves, which means this browser is the slowest.
Not surprisingly.)
<snip>

I am not sure you are putting the blame in the right place. I think
your code might be at fault. For one thing you are re-positioning on
mouse move events and that is asking a great deal of the browser
because mouse moves events happen every time the mouse transitions
from one pixel on the screen to the next. If you move an element the
browser should attempt to re-draw the display and doing that for every
pixel transition can be a lot of work.

And there is the question of whether mouse moves are queued up while
the browser is busily attempting to execute your javascript and then
re-draw the screen. If they are not queued up then the browser is
going to find it easy to keep up, but if they are queued up then as
soon as the browser fails to finish its javascript execution and re-
drawing effort before the next mouse move it is on the path to
becoming bogged down. The result might easily be choppy animation
(with the CPU at 100% while it is going on).

You also appear to be monitoring mouse moves on the object being
dragged, so whenever the mouse gets outside of that elements it stops
receiving move events.

Personally, when I do drag-drop type stuff I activate document level
mouse move and mouse up (to stop the drag) listeners and start a
setTimeout based process that does the actual animation. The document
level mouse move listener does no more than record the current
position of the mouse (so it is very fast) and then when the
setTimeout process is triggered it reads the last recorded mouse
position and positions whatever is being dragged. With the document
level mouse up event stopping the setTimeout process, executing it one
last time so that the final position is applied. This results in the
browser only being asked/expected to re-draw at the animation rate not
for each and every pixel transition of the mouse.

Remember that movies run at 24 frames per second and persistence of
vision still leaves them looking smooth. That is one frame about every
42 milliseconds, so achieving that in a browser will be quite good
enough for a human user (and a lot less work for the browser).
 
M

Martin Rinehart

On Dec 10, 5:18 pm, Martin Rinehart wrote:

I am not sure you are putting the blame in the right place. I think
your code might be at fault.

That might be true, but the drags are silky smooth in every non MSIE
browser. My experience has been that mouse moves are not fired and
queued, thank goodness. As a guess, they are fired on return from the
handler.

By the way, the "dragged out" (moving mouse faster than moving
draggable) thing was an early problem, solved by the penumbra. It's a
giant table stretched over the entire screen. It gets mousemove and
mouseup events and passes them along to the draggable.

You raise a serious issue, however. If the drags aren't nice then
drags are not a design option for those who choose to (are forced to?)
support MSIE. Unless your site is totally geek (or your audience
devoted enough so you can push them into a browser) good MSIE
performance is a must.

I've got two goals, here. One is that no one should have to write yet-
another-drag routine. It should be something you Google, grab and be
done with. The other is infrastructure for a hobby project for myself.
That project is based on <canvas> so for me, MSIE is irrelevant.
 
R

rf

Martin Rinehart said:
Thank you, Henry! Everybody else who has to support MSIE should also
thank you. Made that one change and it worked in MSIE. (By far the
most jagged drag, which means we're handling fewer mouse moves, which
means this browser is the slowest. Not surprisingly.)

Nothing wrong with IE and no it is not slow. It is a repainting effect
caused by your code.

You remove the dragged element from the document.body and then append it
again on each mouse move. The remove and the append each cause IE to
repaint, a reasonable thing to to as you have just removed something.

Comment the following lines like so, for example:
// document.body.removeChild( red.me );
// document.body.appendChild( red.me );
and all works fine with IE. You don't need to remove an element from the
body to change its position.

I have not looked closely at your code but what is this penumbra thing? Why
not just attach event listeners to the body to control the drag?
 
R

RobG

One (the first I spotted) that your code does not work with IE is this
bit of code:-

| this.penumbra =
|     document.createElement( 'table' );
| var td = document.createElement( 'td' );
| var tr = document.createElement( 'tr' );
| tr.appendChild( td );
| this.penumbra.appendChild( tr );

In HTML DOMs (as opposed to XHTML DOMs, and IE is an HTML only
browser) TR elements must be the children of table section elements
(TBODY, THEAD, TFOOT).

Yes, but there is still no need to explicity create a tbody element
when dynamically creating a table, you can use the table.insertRow
method, so:

var tr, td;
this.penumbra =
document.createElement( 'table' );
tr = table.insertRow(-1);
td = tr.insertCell(-1);


and you're done - no need to creat then append (though you still need
to put the table into the document).
 
R

rf

Martin Rinehart said:
Thanks to much help from this group I've been able to write
generalized drag code. It's here:

http://www.martinrinehart.com/examples/js-drag-code.html

Just has another play.

Mousedown on red square. Move mouse outside browser window. Red square
stalls at border. Good.

Mouseup outside browser window. Move mouse back into browser window *with no
buttons pressed*. Red square is still dragging.

If you attach the events to the document body this does not happen. The
mouseup will be caught.
 
M

Martin Rinehart

rf said:
... and all works fine with IE.

You are absolutely right. Went there. Did that. All is now fine in IE
and I didn't have to muck with timeout or interval. Many thanks.

Penumbra is a big 'glass ceiling' over everything else
(zIndex=1000000). If you drag really fast it is possible to get the
mouse out of the draggable. mouseup then might happen and be consumed
by some other element. Penumbra prevents that.
 
D

David Mark

You are absolutely right. Went there. Did that. All is now fine in IE
and I didn't have to muck with timeout or interval. Many thanks.

Penumbra is a big 'glass ceiling' over everything else
(zIndex=1000000).

Over everything except elements with higher z-indeces.
If you drag really fast it is possible to get the
mouse out of the draggable. mouseup then might happen and be consumed
by some other element. Penumbra prevents that.

As mentioned, it is completely unnecessary.
 
M

Martin Rinehart

As mentioned, it [penumbra] is completely unnecessary.

If listeners are attached to body, what happens on a fast drag when
mouseup occurs over another element listening for mouseup?
 
D

David Mark

As mentioned, it [penumbra] is completely unnecessary.

If listeners are attached to body, what happens on a fast drag when
mouseup occurs over another element listening for mouseup?

Don't use the body element (it doesn't cover the entire document in
all cases.) Use the document object. And events bubble, so there are
no worries unless your script gets in the way by stopping propagation
at some point (don't do that.)
 
M

Martin Rinehart

Don't use the body element (it doesn't cover the entire document in
all cases.)  Use the document object.  And events bubble, so there are
no worries unless your script gets in the way by stopping propagation
at some point (don't do that.)

I'm worried that someone else's script will act on and consume the
mouseup.
 
D

David Mark

I'm worried that someone else's script will act on and consume the
mouseup.

I'm worried your "glass" will cause more problems than it solves.
But, if you must, use a div not a table.
 

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
474,005
Messages
2,570,264
Members
46,859
Latest member
HeidiAtkin

Latest Threads

Top