Forum Moderators: open

Message Too Old, No Replies

DHTML dynamic element drag and drop

Half programmed even in IE, need a little help with mid/end issues

         

JAB Creations

8:40 am on Sep 2, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I decided to try and write my own dynamic element drag and drop script since I'm having a blast coding JavaScript without any bloated framework (they are all bloated). Please simply don't mention any frameworks by name, those trying to figure out what I'm doing will very likely use the dash operator to reduce the obnoxious number of framework dependent page results and they're getting really bad when I search for JavaScript articles myself.

So my goal is actually pretty simple, when you click on any element the script uses substr to look for two things. First it looks at the first four characters of the className and if the string=='drag' then the second part of the className ( <element class="drag dragged_element" /> ) is the element's ID that will be dragged; obviously the element clicked on will be the "dragger" element. The point of this is to not write a static script for every instance of applying a drag and drop and using the XHTML's class value (className) as the parameters for the script to handle things.

In my testing I actually got further then I expected. I've been testing with a textarea that updates the coordinates. I've gotten as far as the coordinates updating while the mouse is moving though only if the mouse button is still being held down.

Problem One
I can't obviously use the cursor's direct coordinates to change the left/top positioning of the dragged element's coordinates. I'm not entirely sure how to fix this offset so-to-speak though I'll continue to mess around with it however what I've been trying to do is dependent on problem number two...

Problem Two
I code CSS level 1 correctly so I end up using percentages very often. Now granted positioning is part of CSS level 2 and higher though what really ultimately matters here is that quite often I'll be dealing with percentage references rather then actual pixels at least until the dragged element has been dragged around.

In the code below I've started making attempts at getting the number of pixels for the left and top positioning of the dragged element's coordinates. The problem is that in a couple browsers I recall getting the percentage (50% for centered) versus the actual number of pixels for the left positioning. I'm guessing that if I encounter the percentage and there is no simple way around this then I'll have to do the math with the body element's clientWidth, divide by 2 for 50%, etc though keeping all the data strictly dynamic and not inserting a single static number will likely increase the complexity beyond sanity hence the main reason for posting this on the forums.

Any way here is the code that I've come up with so far. There is a textarea that I'm using to give me a visual on the coordinates of the mouse as well as some other information that I'm figuring out though obviously don't want to use the alert method with. Thoughts please?

- John

var drag = new function() {this.drag = 0;}
document.onmousedown=function(e) {drag.drag = 1; move(e);};
document.onmousemove=function(e) {if (drag.drag==1) {move(e);}};
document.onmouseup=function() {drag.drag = 0; document.getElementById('##########textarea_element_id_goes_here').value='0';};


function move(e)
{
if (!e) {e = window.event;}//IE6

if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

if (t.className.substr(0,4)=='drag')
{
var dragged = document.getElementById(t.className.substr(5));

if (!ie)
{
var l3 = parseInt(window.getComputedStyle(dragged,null).getPropertyValue('left'));
var t3 = parseInt(window.getComputedStyle(dragged,null).getPropertyValue('top'))
}
else
{
var l3 = dragged.currentStyle.left;
var t3 = dragged.currentStyle.top;
}

//Textarea below used as a status to visually determine what is happening.
document.getElementById('##########textarea_element_id_goes_here').value=e.clientX+'\n'+e.clientY+'\n'+l3+'\n'+t3;

var re = false;
}
else {re = true;}

return re;
}

JAB Creations

10:10 am on Sep 2, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



IE and Safari are the two returning the percentage however I found the properties that I need, offsetLeft and offsetTop.

This works in all four browsers including IE6.

- John

var l3 = dragged.offsetLeft;
var t3 = dragged.offsetTop;

Fotiman

3:05 pm on Sep 2, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Hi John,

A couple of notes:

var drag = new function() {this.drag = 0;}

variable drag gets assigned the value (an object) of calling 'new' with an anonymous function (the constructor), which results in drag having an object with property 'drag' = 0. A more efficient way to write this would be:


var drag = {drag: 0};

Using an object literal will save the overhead of calling 'new', and has the added benefit of smaller code.


if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

You may want to change it slightly to this:

var ie, t;
if (e.target) {t = e.target; ie = false;}
else if (e.srcElement) {t = e.srcElement; ie = true;}
if (t.nodeType == 3) { // Safari text node bug
t = t.parentNode;
}

Declare your variables at the top of the function instead of when you first use them. This can prevent some errors where you might try to redeclare a variable. Likewise, I would move the l3 and t3 declarations to the top as well. Also, it reduces the size of the script because the 'var' is only included once for each variable instead of once in each block. The other change I added was a bug fix for Safari to prevent text nodes from being the target instead of the actual element.


if (t.className.substr(0,4)=='drag')

The problem with this approach is that it will also match strings like "dragon" or "dragster". If you're going to use substr, you might want to do something like this instead:


if (t.className.substr(0,5)=='drag ')

In other words, search for whitespace after 'drag'. Note, that example won't match whitespace other than a space character (tabs, etc.), so it could be improved even further. Just wanted to get you thinking about it. :)


var dragged = document.getElementById(t.className.substr(5));

Again, that assumes that there will only ever be 2 className strings. You might want to further enhance this to handle multiple classNames. Also, if dragged is null (the element was not found) then your next code will error. You might want to find a way to handle that.

Here's a test case with your code slightly reworked:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div {
background: #ccc;
border: 1px solid #000;
}
#myForm {
float: left;
width: 250px;
}
</style>
<title>Drag Drop Test</title>
</head>
<body>
<form action="">
<div id="myForm">
<textarea id="logging" rows="3000" cols="24"></textarea>
</div>
</form>
<div class="drag foo" id="foo">Foo</div>
<div class="drag bar" id="bar">Bar</div>
<script type="text/javascript">
var d = document,
drag = {drag: 0},
logging = d.getElementById('logging');
d.onmousedown=function(e) {drag.drag = 1; move(e);};
d.onmousemove=function(e) {if (drag.drag == 1) {move(e);}};
d.onmouseup=function() {drag.drag = 0; logging.value += '0\n';};
function move(e) {
var dragged,
ie,
t,
l3,
t3,
re = true;
if (!e) {e = window.event;}//IE6
if (e.target) {t = e.target; ie = false;}
else if (e.srcElement) {t = e.srcElement; ie = true;}
if (t.className.substr(0,5)=='drag ') {
dragged = document.getElementById(t.className.substr(5));
if (!ie) {
l3 = dragged.offsetLeft;
t3 = dragged.offsetTop;
}
else {
l3 = dragged.currentStyle.left;
t3 = dragged.currentStyle.top;
}
//Textarea below used as a status to visually determine what is happening.
logging.value += 'mouse[x,y]=[' +e.clientX+','+e.clientY+']\nelement[x,y]=['+l3+','+t3+']\n';
re = false;
}
return re;
}
</script>
</body>
</html>

JAB Creations

2:58 am on Sep 3, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Optimization works best after scripts work as desired. ;-)

To be honest I could use a little guidance at this point, I've looked at dozens of tutorials and nothing specifically works with moving elements by handles. I think I have to somehow determine the relative position of the cursor to the element...I think?

- John

JAB Creations

4:20 am on Sep 3, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Here is an update and a full standalone file that is starting to work. I've got the horizontal movement sort of working (click and hold the left side of the handle to drag). The status textarea has really helped give me an idea of which steps to follow and in which order... :)

- John

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<script type="text/javascript">
//<![CDATA[

var drag = {drag: 0};
document.onmousedown=function(e) {drag.drag = 1; move(e,1);};
document.onmousemove=function(e) {if (drag.drag==1) {move(e,0);}};
document.onmouseup=function() {drag.drag = 0; /*document.getElementById('status').value='0';*/};

var cur_x_init = 0;
var cur_y_init = 0;
var dragged_x_init = 0;
var dragged_y_init = 0;

var dragged_x = 0;
var dragged_y = 0;
var init_dif_x = 0;
var init_dif_y = 0;

function move(e,init)
{
if (!e) {e = window.event;}//IE6 OR: if (document.all) {e = window.event;}//IE6

if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

if (t.className.substr(0,5)=='drag ')
{
var dragged = document.getElementById(t.className.substr(5));

if (init==1)
{
cur_x_init = e.clientX;
cur_y_init = e.clientY;
dragged_x_init = dragged.offsetLeft;
dragged_y_init = dragged.offsetTop;
}
else
{
dragged_x = dragged.offsetLeft;
dragged_y = dragged.offsetTop;

var l = e.clientX - cur_x_init;
var t = e.clientY - cur_y_init;
dragged.style.left = dragged_x_init + l+'px';
//dragged.style.top = dragged_y_init + t+'px';
}
//var xpos = e.clientX-init_dif_x;

document.getElementById('status').value='Init: '+init+'\nCur Init X: '+
cur_x_init+'\nCur Init Y: '+cur_y_init+'\nCur X: '+e.clientX+'\nCur Y: '+
e.clientY+'\nDragged Init X: '+dragged_x_init+'\nDragged Init Y: '+
dragged_y_init+'\nDragged Left: '+dragged_x+'\nDragged Top: '+
dragged_y+'\n'+init_dif_x+'\n'+init_dif_y+'\nxpos = '+xpos;
var re = false;
}
else {re = true;}

return re;
}
//]]>
</script>
<style type="text/css">
#prompts {background: #ccc; border: solid 1px; border-color: #ccc #aaa #aaa #ccc;
height: 398px; left: 50%; margin-top: -198px; margin-left: -369px; position: absolute;
top: 50%; width: 738px; z-index: 3;}
#prompts h2 {background-color: #ddd; height: 20px; line-height: 20px; margin: 0px; text-align: center;}
#prompts h2 span {display: block; text-align: center; width: 738px;}
#prompts textarea {height: 373px; width: 734px;}
</style>
</head>

<body>

<div id="prompts">
<h2><span class="drag prompts">Title - This is the Drag Handle</span></h2>
<textarea id="status"></textarea>
</div>

</body>
</html>

[edited by: Fotiman at 1:45 pm (utc) on Sep 3, 2010]
[edit reason] Breaking up long lines [/edit]

JAB Creations

5:44 am on Sep 3, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I've got the horizontal movement working flawlessly in all browsers now by taking the dragged item's clientWidth, dividing it in half, and adding it to the positioning. I hope the vertical movement is as easy as mimicking the horizontal movement's code. Here is what I have... :)

- John

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<script type="text/javascript">
//<![CDATA[
var drag = {drag: 0};
document.onmousedown=function(e) {drag.drag = 1; move(e,1);};
document.onmousemove=function(e) {if (drag.drag==1) {move(e,0);}};
document.onmouseup=function() {drag.drag = 0; /*document.getElementById('status').value='0';*/};

var cur_x_init = 0;
var cur_y_init = 0;
var cur_x_dif = 0;
var cur_y_dif = 0;
var dragged_x_init = 0;
var dragged_y_init = 0;

var dragged_x = 0;
var dragged_y = 0;
var init_dif_x = 0;
var init_dif_y = 0;

function move(e,init)
{
if (!e) {e = window.event;}//IE6 OR: if (document.all) {e = window.event;}//IE6

if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

if (t.className.substr(0,5)=='drag ')
{
var dragged = document.getElementById(t.className.substr(5));

if (init==1)
{
cur_x_init = e.clientX;
cur_y_init = e.clientY;
dragged_x_init = dragged.offsetLeft;
dragged_y_init = dragged.offsetTop;


}
else
{
dragged_x = dragged.offsetLeft;
dragged_y = dragged.offsetTop;
cur_x_dif = e.clientX - cur_x_init;
cur_y_dif = e.clientY - cur_y_init;

dragged_half = dragged.clientWidth/2;
dragged_x_new = dragged_x_init+cur_x_dif+dragged_half;
dragged.style.left = dragged_x_new+'px';
//dragged.style.top = dragged_y_init + t+'px';
}

document.getElementById('status').value='cur_x_init = '+cur_x_init+'\ncur_x = '+
e.clientX+'\ncur_x_dif = '+cur_x_dif+'\ndragged_x_init = '+dragged_x_init+'\ndragged_x_new = '+dragged_x_new;
//document.getElementById('status').value='Init: '+init+'\nCur Init X: '+
//cur_x_init+'\nCur Init Y: '+cur_y_init+'\nCur X: '+e.clientX+'\nCur Y: '+
//e.clientY+'\nDragged Init X: '+dragged_x_init+'\nDragged Init Y: '+
//dragged_y_init+'\nDragged Left: '+dragged_x+'\nDragged Top: '+
//dragged_y+'\n'+init_dif_x+'\n'+init_dif_y;
var re = false;
}
else {re = true;}

return re;
}
//]]>
</script>
<style type="text/css">
#prompts {background: #ccc; border: solid 1px; border-color: #ccc #aaa #aaa #ccc;
height: 398px; left: 50%; margin-top: -198px; margin-left: -369px; position: absolute;
top: 50%; width: 738px; z-index: 3;}
#prompts h2 {background-color: #ddd; height: 20px; line-height: 20px; margin: 0px; text-align: center;}
#prompts h2 span {display: block; text-align: center; width: 738px;}
#prompts textarea {height: 373px; width: 734px;}
</style>
</head>

<body>

<div id="prompts">
<h2><span class="drag prompts">Title - This is the Drag Handle</span></h2>
<textarea id="status"></textarea>
</div>

</body>
</html>

[edited by: Fotiman at 1:47 pm (utc) on Sep 3, 2010]
[edit reason] Breaking up long lines [/edit]

JAB Creations

7:43 am on Sep 3, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



SUCCESS! I don't consider this final though I do consider this a reasonably working example that's even colored with four separate layers that can be dragged around, and is very backwards compatible. I'll add a z-index option to prevent the layers from bumping in to each other in the future though I can't think of anything else. The entire working file is a little over 3KB.

I tested this and it works fine in...
IE 5.5+ (Will likely work in IE 5.0)
Opera 7.5+
Firefox 1.0+ / Gecko 1.7+ (Gecko 1.6 was buggy, might not support clientWidth property I think offhand)
Safari 5, didn't test this with any older versions though I'm sure this should work in Safari 3.0 and likely 2.0.

I'm open to suggestions on how to improve this still though I wouldn't consider this truly optimized until you can drop a function name inside of the onload event's anonymous function so I think it's still a bit "messy". I'll likely post an updated version sometime tomorrow night. :)

- John

dhtml.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<script type="text/javascript">
//<![CDATA[
var drag = {drag: 0};
document.onmousedown=function(e) {drag.drag = 1; move(e,1);};
document.onmousemove=function(e) {if (drag.drag==1) {move(e,0);}};
document.onmouseup=function() {drag.drag = 0; /*document.getElementById('status').value='0';*/};

var cur_x_init = 0;
var cur_y_init = 0;
var cur_x_dif = 0;
var cur_y_dif = 0;
var dragged_x_init = 0;
var dragged_y_init = 0;

function move(e,init)
{
if (!e) {e = window.event;}//IE6 OR: if (document.all) {e = window.event;}//IE6

if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

if (t.className.substr(0,5)=='drag ')
{
var dragged = document.getElementById(t.className.substr(5));

if (init==1)
{
cur_x_init = e.clientX;
cur_y_init = e.clientY;
dragged_x_init = dragged.offsetLeft;
dragged_y_init = dragged.offsetTop;

var dragged_x_new = 0;
var dragged_y_new = 0;
}
else
{
cur_x_dif = e.clientX - cur_x_init;
cur_y_dif = e.clientY - cur_y_init;

var dragged_x_half = dragged.clientWidth/2;
var dragged_y_half = dragged.clientHeight/2;
var dragged_x_new = dragged_x_init+cur_x_dif+dragged_x_half;
var dragged_y_new = dragged_y_init+cur_y_dif+dragged_y_half;
dragged.style.left = dragged_x_new+'px';
dragged.style.top = dragged_y_new+'px';
}

document.getElementById('status').value='ID = '+
t.className.substr(5)+'\ncur_x_init = '+cur_x_init+'\ncur_x = '+
e.clientX+'\ncur_x_dif = '+cur_x_dif+'\ndragged_x_init = '+
dragged_x_init+'\ndragged_x_new = '+dragged_x_new;
var re = false;
}
else {re = true;}

return re;
}
//]]>
</script>
<style type="text/css">
body {overflow: hidden;}
.drag {cursor: move;}
div.prompts {background: #ccc; border: solid 1px; border-color: #ccc #aaa #aaa #ccc;
height: 398px; margin-top: -198px; margin-left: -369px; position: absolute;
width: 738px;}
div.prompts h2 {background-color: #ddd; height: 20px; line-height: 20px; margin: 0px; text-align: center;}
div.prompts h2:hover {background-color: #fc6;}
div.prompts h2 span {display: block; text-align: center; width: 738px;}
div.prompts textarea {height: 373px; width: 732px;}
#prompt1 {left: 50%; top: 50%; z-index: 3;}
#prompt2 {background-color: #fdd; left: 55%; top: 55%; z-index: 2;}
#prompt3 {background-color: #dfd; left: 60%; top: 60%; z-index: 1;}
#prompt4 {background-color: #ddf; left: 65%; top: 65%; z-index: 0;}
</style>
<!--[if lte IE 7]><style type="text/css">div.prompts textarea {float: right;}</style><![endif]-->
</head>

<body>

<div class="prompts" id="prompt1"><h2><span class="drag prompt1">Prompt 1 - This is the Drag Handle</span></h2><textarea id="status"></textarea></div>

<div class="prompts" id="prompt2"><h2><span class="drag prompt2">Prompt 2 - This is the Drag Handle</span></h2></div>

<div class="prompts" id="prompt3"><h2><span class="drag prompt3">Prompt 3 - This is the Drag Handle</span></h2></div>

<div class="prompts" id="prompt4"><h2><span class="drag prompt4">Prompt 4 - This is the Drag Handle</span></h2></div>

</body>
</html>

[edited by: Fotiman at 1:48 pm (utc) on Sep 3, 2010]
[edit reason] Breaking up long lines [/edit]

Fotiman

2:40 pm on Sep 3, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I took a stab at making it a little more efficient. Note, it could still be improved, this is just a starting point. Also note, if you move the mouse really fast while dragging something, you can lose that item.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<style type="text/css">
body {
overflow: hidden;
}
.drag {
cursor: move;
}
div.prompts {
background: #ccc;
border: solid 1px;
border-color: #ccc #aaa #aaa #ccc;
height: 398px;
margin-top: -198px;
margin-left: -369px;
position: absolute;
width: 738px;
}
div.prompts h2 {
background-color: #ddd;
height: 20px;
line-height: 20px;
margin: 0px;
text-align: center;
}
div.prompts h2:hover {
background-color: #fc6;
}
div.prompts h2 span {
display: block;
text-align: center;
width: 738px;
}
div.prompts textarea {
height: 373px;
width: 732px;
}
#prompt1 {
left: 50%;
top: 50%;
z-index: 3;
}
#prompt2 {
background-color: #fdd;
left: 55%;
top: 55%;
z-index: 2;
}
#prompt3 {
background-color: #dfd;
left: 60%;
top: 60%;
z-index: 1;
}
#prompt4 {
background-color: #ddf;
left: 65%;
top: 65%;
z-index: 0;
}
</style>
<!--[if lte IE 7]>
<style type="text/css">div.prompts textarea {float: right;}</style>
<![endif]-->
</head>
<body>
<div class="prompts" id="prompt1">
<h2><span class="drag prompt1">Prompt 1 - This is the Drag Handle</span></h2>
<textarea id="status"></textarea>
</div>
<div class="prompts" id="prompt2">
<h2><span class="drag prompt2">Prompt 2 - This is the Drag Handle</span></h2>
</div>
<div class="prompts" id="prompt3">
<h2><span class="drag prompt3">Prompt 3 - This is the Drag Handle</span></h2>
</div>
<div class="prompts" id="prompt4">
<h2><span class="drag prompt4">Prompt 4 - This is the Drag Handle</span></h2>
</div>
<script type="text/javascript">
//<![CDATA[
JAB_DD = {
init: function () {
var cur_x_dif = 0,
cur_x_init = 0,
cur_y_dif = 0,
cur_y_init = 0,
d = document,
drag = {drag: 0},
dragged_x_init = 0,
dragged_y_init = 0;
function move(e, init) {
var dragged,
dragged_x_half,
dragged_x_new,
dragged_y_half,
dragged_y_new,
ie,
re = true,
t;
if (!e) {
e = window.event;
}//IE6 OR: if (document.all) {e = window.event;}//IE6
if (e.target) {
t = e.target;
ie = false;
}
else if (e.srcElement) {
t = e.srcElement;
ie = true;
}
if (t.className.substr(0,5) == 'drag ') {
dragged = d.getElementById(t.className.substr(5));
if (init == 1) {
cur_x_init = e.clientX;
cur_y_init = e.clientY;
dragged_x_init = dragged.offsetLeft;
dragged_y_init = dragged.offsetTop;
dragged_x_new = 0;
dragged_y_new = 0;
}
else {
cur_x_dif = e.clientX - cur_x_init;
cur_y_dif = e.clientY - cur_y_init;
dragged_x_half = dragged.clientWidth / 2;
dragged_y_half = dragged.clientHeight / 2;
dragged_x_new = dragged_x_init + cur_x_dif + dragged_x_half;
dragged_y_new = dragged_y_init + cur_y_dif + dragged_y_half;
dragged.style.left = dragged_x_new + 'px';
dragged.style.top = dragged_y_new + 'px';
}
d.getElementById('status').value = 'ID = ' +
t.className.substr(5) + '\ncur_x_init = ' + cur_x_init +
'\ncur_x = ' + e.clientX +
'\ncur_x_dif = ' + cur_x_dif +
'\ndragged_x_init = ' + dragged_x_init +
'\ndragged_x_new = ' + dragged_x_new;
re = false;
}
return re;
}
// Attach event listeners
// TODO: Replace these event handlers with event listeners
d.onmousedown = function (e) {
drag.drag = 1;
move(e, 1);
};
d.onmousemove = function (e) {
if (drag.drag == 1) {
move(e, 0);
}
};
d.onmouseup = function () {
drag.drag = 0;
/*document.getElementById('status').value='0';*/
};
}
};
window.onload = JAB_DD.init;
//]]>
</script>
</body>
</html>

JAB Creations

1:24 am on Sep 4, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks for your suggestions Fotiman. I'm not familiar with the init method and setting everything to a function however it still worked in IE 5.5, Opera 7.5, and Firefox 1.0 so that's what ultimately mattered. I keep my scripts in the head element and that practice won't change with me since it requires stricter coding habits though we don't have to debate any of that. I can see the benefits of splitting the move() function in to two functions instead, I think that is part of what you were thinking in regards to further optimization?

Right now I'm stuck with an IE6-7-8 bug where it's absolutely refusing to acknowledge the dragged_z variable regardless of it's placement, scope, etc. Alerting the typeof dragged_z shows IE doesn't see it, I've tried converting it from a number to a string, moving it outside of the object entirely, etc.

In IE9 and all other browsers however the zIndex adaptation works wonderfully and the layers no longer "bump" in to each other. :)

- John

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<script type="text/javascript">
//<![CDATA[
JAB_DD = {
init: function ()
{
var cur_x_dif = 0,
cur_x_init = 0,
cur_y_dif = 0,
cur_y_init = 0,
d = document,
drag = {drag: 0},
dragged_x_init = 0,
dragged_y_init = 0,
dragged_z = 0;

function move(e,init)
{
var dragged,dragged_x_half,dragged_x_new,dragged_y_half,dragged_y_new,ie,re=true,t,z;

if (!e) {e=window.event;}//IE6 OR: if (d.all) {e = window.event;}//IE6

if (e.target) {t=e.target; ie = false;}
else if (e.srcElement) {t=e.srcElement; ie = true;}

if (t.className.substr(0,5)=='drag ')
{
dragged = d.getElementById(t.className.substr(5));

if (init!=2)
{
if (init==1)
{
cur_x_init=e.clientX;
cur_y_init=e.clientY;
dragged_x_init=dragged.offsetLeft;
dragged_y_init=dragged.offsetTop;
dragged_x_new=0;
dragged_y_new=0;

if (dragged_z!=9001)
{
if (window.getComputedStyle) {z = d.defaultView.getComputedStyle(dragged,null).getPropertyValue('z-index');}
else if (dragged.currentStyle) {z = dragged.currentStyle['z-index'];}

if (z!=9001) {dragged_z=z;}

dragged = dragged.style.zIndex=9001;
}
}
else
{
cur_x_dif=e.clientX-cur_x_init;
cur_y_dif=e.clientY-cur_y_init;
dragged_x_half=dragged.clientWidth/2;
dragged_y_half=dragged.clientHeight/2;
dragged_x_new=dragged_x_init+cur_x_dif+dragged_x_half;
dragged_y_new=dragged_y_init+cur_y_dif+dragged_y_half;
dragged.style.left=dragged_x_new+'px';
dragged.style.top=dragged_y_new+'px';
}

d.getElementById('status').value = 'ID = '+t.className.substr(5)+'\ncur_x_init = '+cur_x_init+'\ncur_x = '+
e.clientX+'\ncur_x_dif = '+cur_x_dif+'\ndragged_x_init = '+
dragged_x_init+'\ndragged_x_new = '+dragged_x_new+'\ndragged_z = '+dragged_z;
re = false;
}
else {dragged.style.zIndex=dragged_z;}
}
return re;
}

// Attach event listeners
// TODO: Replace these event handlers with event listeners
d.onmousedown = function (e) {drag.drag=1; move(e,1);};
d.onmousemove = function (e) {if (drag.drag==1) {move(e,0);}};
d.onmouseup = function (e) {drag.drag=0;move(e,2);};
}
};
window.onload = JAB_DD.init;
//]]>
</script>
<style type="text/css">
body {overflow: hidden;}
.drag {cursor: move;}
div.prompts {background: #ccc; border: solid 1px; border-color: #ccc #aaa #aaa #ccc;
height: 398px; margin-top: -198px; margin-left: -369px; position: absolute; width: 738px;}
div.prompts h2 {background-color: #ddd; height: 20px; line-height: 20px; margin: 0px; text-align: center;}
div.prompts h2:hover {background-color: #fc6;}
div.prompts h2 span {display: block; text-align: center; width: 738px;}
div.prompts textarea {height: 373px; width: 732px;}
#prompt1 {left: 50%; top: 50%; z-index: 3;}
#prompt2 {background-color: #fdd; left: 55%; top: 55%; z-index: 2;}
#prompt3 {background-color: #dfd; left: 60%; top: 60%; z-index: 1;}
#prompt4 {background-color: #ddf; left: 65%; top: 65%; z-index: 0;}
</style>
<!--[if lte IE 7]><style type="text/css">div.prompts textarea {float: right;}</style><![endif]-->
</head>

<body>

<div class="prompts" id="prompt1">
<h2><span class="drag prompt1">Prompt 1 - This is the Drag Handle</span></h2><textarea id="status"></textarea>
</div>

<div class="prompts" id="prompt2">
<h2><span class="drag prompt2">Prompt 2 - This is the Drag Handle</span></h2>
</div>

<div class="prompts" id="prompt3">
<h2><span class="drag prompt3">Prompt 3 - This is the Drag Handle</span></h2>
</div>

<div class="prompts" id="prompt4">
<h2><span class="drag prompt4">Prompt 4 - This is the Drag Handle</span></h2>
</div>

</body>
</html>

JAB Creations

2:01 am on Sep 5, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I got it working fine in IE now. I tried this with my copy that didn't take your optimization just in case I was doing something wrong with your code. I still have to optimize this in to a self-contained function though it is now fully functional even in IE 5.5+. :)

- John

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>DHTML</title>
<script type="text/javascript">
//<![CDATA[
var drag = new function() {this.drag = 0;}
document.onmousedown=function(e) {drag.drag = 1; move(e,1);};
document.onmousemove=function(e) {if (drag.drag==1) {move(e,0);}};
document.onmouseup = function(e) {drag.drag=0;move(e,2);};

var cur_x_init = 0;
var cur_y_init = 0;
var cur_x_dif = 0;
var cur_y_dif = 0;
var dragged_x_init = 0;
var dragged_y_init = 0;
var dragged_z = 0;

function move(e,init)
{
var dragged,dragged_x_half,dragged_x_new,dragged_y_half,dragged_y_new,ie,re=true,t,z;

if (!e) {e = window.event;}//IE6 OR: if (document.all) {e = window.event;}//IE6

if (e.target) {var t = e.target; var ie = false;}
else if (e.srcElement) {var t = e.srcElement; var ie = true;}

if (t.className.substr(0,5)=='drag ')
{
dragged = document.getElementById(t.className.substr(5));

if (init!=2)
{
if (init==1)
{
cur_x_init=e.clientX;
cur_y_init=e.clientY;
dragged_x_init=dragged.offsetLeft;
dragged_y_init=dragged.offsetTop;
dragged_x_new=0;
dragged_y_new=0;

if (dragged_z!=9001)
{
if (window.getComputedStyle) {z = document.defaultView.getComputedStyle(dragged,null).getPropertyValue('z-index');}
else if (dragged.currentStyle) {z = dragged.currentStyle.zIndex;}

if (z!=9001) {dragged_z=z;}

dragged = dragged.style.zIndex=9001;
}
}
else
{
cur_x_dif=e.clientX-cur_x_init;
cur_y_dif=e.clientY-cur_y_init;
dragged_x_half=dragged.clientWidth/2;
dragged_y_half=dragged.clientHeight/2;
dragged_x_new=dragged_x_init+cur_x_dif+dragged_x_half;
dragged_y_new=dragged_y_init+cur_y_dif+dragged_y_half;
dragged.style.left=dragged_x_new+'px';
dragged.style.top=dragged_y_new+'px';
}

document.getElementById('status').value = 'ID = '+t.className.substr(5)+'\ncur_x_init =
'+cur_x_init+'\ncur_x = '+e.clientX+'\ncur_x_dif =
'+cur_x_dif+'\ndragged_x_init =
'+dragged_x_init+'\ndragged_x_new =
'+dragged_x_new+'\ndragged_z = '+dragged_z;
re = false;
}
else {document.getElementById(t.className.substr(5)).style.zIndex=dragged_z;}
}
else {re = true;}

return re;
}
//]]>
</script>
<style type="text/css">
body {overflow: hidden;}
.drag {cursor: move;}
div.prompts {background: #ccc; border: solid 1px; border-color: #ccc #aaa #aaa #ccc; height: 398px;
margin-top: -198px; margin-left: -369px; position: absolute; width: 738px;}
div.prompts h2 {background-color: #ddd; height: 20px;
line-height: 20px; margin: 0px; text-align: center;}
div.prompts h2:hover {background-color: #fc6;}
div.prompts h2 span {display: block; text-align: center; width: 738px;}
div.prompts textarea {height: 373px; width: 732px;}
#prompt1 {left: 50%; top: 50%; z-index: 3;}
#prompt2 {background-color: #fdd; left: 55%; top: 55%; z-index: 2;}
#prompt3 {background-color: #dfd; left: 60%; top: 60%; z-index: 1;}
#prompt4 {background-color: #ddf; left: 65%; top: 65%; z-index: 0;}
</style>
<!--[if lte IE 7]><style type="text/css">div.prompts textarea {float: right;}</style><![endif]-->
</head>

<body>

<div class="prompts" id="prompt1"><h2><span class="drag prompt1">Prompt 1 - This is the Drag Handle</span></h2><textarea id="status"></textarea></div>

<div class="prompts" id="prompt2"><h2><span class="drag prompt2">Prompt 2 - This is the Drag Handle</span></h2></div>

<div class="prompts" id="prompt3"><h2><span class="drag prompt3">Prompt 3 - This is the Drag Handle</span></h2></div>

<div class="prompts" id="prompt4"><h2><span class="drag prompt4">Prompt 4 - This is the Drag Handle</span></h2></div>

</body>
</html>