SubMenu
Tutorial Stats
Made by:
Forter
On:
25-5-2007 20:06
Total views:
4720
Replys:
0
Catagory:
Advanced
Type:
JavaScript
Subject:
Dynamic
Advertisement
JavaScript Tutorial

Drag and Drop in JavaScript

This tutorial will show you the basics of drag and drop in javascript, this means that an object moves along with the mouse as long as the mouse button is pressed. We will make an example in which you can drag a card across the screen (see image on the right side of this text). The first part of the tutorial is Internet Explorer only, so i suggest that you use IE if you want to try it yourself. The Netscape/FireFox part will be added in the last step. This will keep the example code more readable.


Drag and drop

The idea behind this is pretty hard to explain in words, so we will create the basic html page and add a little bit to it in every step. First step is off course to display the object on the screen, we will do this with a simple <div> tag. The markup will be defined with CSS and the image we use can be found at files/tut_8/ace.gif. This is enough info for now, so lets create the html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Drag and Drop</title>
    <style>
    .ace {
        display: block;
        width: 74px;
        height: 102px;
        background: url(ace.gif);
        cursor: hand;
        position: relative;
    }
    </style>
  </head>
  <body>
    <div class=ace></div>
  </body>
</html>

This defines a simple class and shows it on the screen. I know that cursor:hand is not really valid, and 74 x 102 pixels is a bit strange, but its only an example. The important part is that we have set the position to relative, this means that we can change the position of the div relative to the original position on the page. You could also use position:absolute, which would be better in game setting.


Connecting with javascript

To make the drag and drop possible, we need some sort of connection between the mouse and the html. Javascript has got very easy event handlers so lets talk about that first.

The events we need to capture are: mouse movement, mouse button down and mouse button up. This is done in the following way (in IE!).

<script>
function mouseMove() {
    x = event.clientX;
    y = event.clientY;
}

function mouseDown() {

}

function mouseUp() {

}

document.onmousemove = mouseMove;
document.onmousedown = mouseDown;
document.onmouseup = mouseUp;
</script>

The first part exists out of three different functions. I just used logical names like mouseUp, but you can use any name you want. The first function set the current x and y coordinate of the mouse to the variables x and y. The global event.clientX holds the current position of the mouse x, relative to the top-left corner of the window, so we just set it to our own variable in this case. The function mouseDown and mouseUp are empty so nothing will happen there.

We now have our function but they wont do anything until we tell the browser to call them whenever a mouse event occurs. The document.onmousemove event calls the function mouseMove() when the user moves the mouse over the document. Note that the document.onmousemove has no kapital letters and that the mouseMove has no () things. Same thing goes for the mouseUp and mouseDown.


Changing the position

We can also change the position of the div with javascript, we do this with document.getElementById(div_id).style.top. This just gets a div from the document with a certain id, then it goes to the style of div and takes the top part of it. We can read and write to this value so we can move the div using this method. First step is to give our div and id by using the id attribute:

<div class=ace id=ace></div>

This gives the div an id "ace", which is the same as the class name but that doesn't matter. If we want to assing a value to the position of the div, then we would use the following code:

document.getElementById("ace").style.top = NewY;
document.getElementById("ace").style.left = NewX;

We can also retrieve the current value from the div which is pretty usefull in our case. This is a bit more difficult because the value is actually a string with "px" behind it. If we want to get the value then we have to get rid of the "px", we do this by using the replace() function.

OldY = (document.getElementById("ace").style.top).replace("px","");
OldX = (document.getElementById("ace").style.left).replace("px","");


The basic setup

We can now place a part of the javascript in the html page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Drag and Drop</title>
    <style>
    .ace {
        display: block;
        width: 74px;
        height: 102px;
        background: url(ace.gif);
        cursor: hand;
        position: relative;
    }
    </style>
    <script>
    function startDrag(id) {
        
    }
	
    function mouseMove() {
        
    }
	
    function endDrag() {
        
    }
	
    document.onmousemove = mouseMove;
    document.onmouseup = endDrag;
    </script>
  </head>
  <body>
    <div class=ace id=ace onmousedown="startDrag('ace')"></div>
  </body>
</html>

We now have a function startDrag, which is called when user presses a mouse button over the ace div. Note we send the div id to the function, this makes it very easy to add another drag and drop item.

The next function is the mouseMove function which is called whenever the mouse moves over the document.

And finally the endDrag function which is called whenever a mouse button is released over the document. You could also use the onmouseup attribute on the div, but the mouse might move a bit faster than the div, so the function would not be called in all cases.


Moving the card

Now lets move the card around. We start by creating a global variable named "DragId" with a value of "". We then assing the div id to it in the startDrag() function, and set the value to "" again in the endDrag() function. This means that the value of DragId is different than "" whenever an object is dragged, so we use that knowledge in the mouseMove() function to assing the x and y value of the cursor to the x and y value of the card.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Drag and Drop</title>
    <style>
    .ace {
        display: block;
        width: 74px;
        height: 102px;
        background: url(ace.gif);
        cursor: hand;
        position: relative;
    }
    </style>
    <script>
    var DragId = "";
	
    function startDrag(id) {
        DragId = id;
    }
	
    function mouseMove() {
        if (DragId != "") {
            document.getElementById(DragId).style.top = event.clientY;
            document.getElementById(DragId).style.left = event.clientX;
        }
    }
	
    function endDrag() {
        DragId = ""; 
    }
	
    document.onmousemove = mouseMove;
    document.onmouseup = endDrag;
    </script>
  </head>
  <body>
    <div class=ace id=ace onmousedown="startDrag('ace')"></div>
  </body>
</html>

We can now move the card, but there is still a problem. The coordinates of the cursor are directly passed to the card, so the card moves to the wrong point when you click on the card. There is off course a very simple solution for this problem so lets see what exactly goes wrong.


If you look at the (crappy) illustration on the left, then you will see that there is a space between the card origin and the mouse pointer, and also between the document origin and the card origin.

The space between the card and the document is something that we want to get rid of, because the origin of the card is relative to its original position. The mouse coordinates are however calculated from the document origin. We can solve this problem by taking the space between the card origin and the cursor in the startDrag() function and then use that in the mouseMove() function to correct the position. We pass the values by using two global variables (OffsetX and OffsetY).












The page will then look like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Drag and Drop</title>
    <style>
    .ace {
        display: block;
        width: 74px;
        height: 102px;
        background: url(ace.gif);
        cursor: hand;
        position: relative;
    }
    </style>
    <script>
    var DragId = "";
    var OffsetY = 0;
    var OffsetX = 0;
	
    function startDrag(id) {
        DragId = id;
        OffsetY = event.clientY - (document.getElementById(DragId).style.top).replace("px","");
        OffsetX = event.clientX - (document.getElementById(DragId).style.left).replace("px","");
    }
	
    function mouseMove() {
        if (DragId != "") {
            document.getElementById(DragId).style.top = event.clientY - OffsetY;
            document.getElementById(DragId).style.left = event.clientX - OffsetX;
            document.selection.empty();
        }
    }
	
    function endDrag() {
        DragId = ""; 
    }
	
    document.onmousemove = mouseMove;
    document.onmouseup = endDrag;
    </script>
  </head>
  <body>
    <div class=ace id=ace onmousedown="startDrag('ace')"></div>
  </body>
</html>

Note that i also added the document.selection.empty() thing in the mouseMove function. This makes sure that nothing is selected when dragging the card around. This example should work now so you can check it out by clicking the following link: files/tut_8/html_ie.html. This only works in Internet Explorer, so move on to the next step if you want to know how we can make it work in Firefox too.



Making it work in Firefox

We now have a working script, but if you open it in firefox then you'll get a lot of errors. This is because firefox (netscape) and Internet Explorer use different event handlers, very handy...

The next piece of code works in both Firefox and Internet Explorer, other browsers will probably not accept it, but you at least cover the three biggest ones.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Drag and Drop</title>
    <style>
    .ace {
        display: block;
        width: 74px;
        height: 102px;
        background: url(ace.gif);
        cursor: hand;
        position: relative;
    }
    </style>
    <script>
    var DragId = "";
    var OffsetY = 0;
    var OffsetX = 0;
    var MouseX = 0;
    var MouseY = 0;
	
    if (navigator.appName == "Microsoft Internet Explorer") {
        var Browser = "IE";
    }
    else {
        var Browser = "FF";
    }
	
    function startDrag(id) {
        DragId = id;
        OffsetY = MouseY - (document.getElementById(DragId).style.top).replace("px","");
        OffsetX = MouseX - (document.getElementById(DragId).style.left).replace("px","");
    }
	
    function mouseMoveIE() {
        MouseX = event.clientX;
        MouseY = event.clientY;
        if (DragId != "") {
            document.getElementById(DragId).style.top = MouseY - OffsetY;
            document.getElementById(DragId).style.left = MouseX - OffsetX;
            document.selection.empty();
        }
    }
	
    function mouseMoveFF(event) {
        MouseX = event.clientX;
        MouseY = event.clientY;
        if (DragId != "") {
            document.getElementById(DragId).style.top = MouseY - OffsetY;
            document.getElementById(DragId).style.left = MouseX - OffsetX;
            window.getSelection().removeAllRanges();
        }
    }
	
    function endDrag() {
        DragId = ""; 
    }
	
    if (Browser == "IE") {
        document.onmousemove = mouseMoveIE;
    }
    else {
        document.onmousemove = mouseMoveFF;
    }
    document.onmouseup = endDrag;
    </script>
  </head>
  <body>
    <div class=ace id=ace onmousedown="startDrag('ace')"></div>
  </body>
</html>

The idea is still pretty much the same, but we check at the start of the script whether the browser is IE of firefox. We than assign the proper function to the event handler at the end of the script. Also note that we place the cursor coordinates in the global MouseX and MouseY variables, this is because the clientX and clientY are not global variables in Firefox. You can see this example on the following page files/tut_8/html.html.

Thats it, I hope that you learned something from this tutorial and I also want to thank you for reading it.