DHTML Tutorial 16
Click and Drag



Click and Drag is the ability to click and drag on an object (usually an image) and deposit it somewhere else on the screen. Here is an example of clicking and dragging, simply click on the useractive logo below and simulaneously drag it around the web page:



In this lesson, we'll add this functionality to our dom.js file to make it easy to enable cross-browser Click and Drag.

Adding Click and Drag to dom.js

Let me start by giving you the outline of how we're going to approack this. First I'm going to give you the code and teach you how to use it, and then I'll go through the code and explain how it works. okay? (please say okay). GOOD! Again, let's recall our latest encarnation of dom.js:

dom.js so far:
nav = document.layers; IE4 = document.all; isNav4 = (nav) ? true : false; isIE4 = (IE4) ? true : false; if(isNav4){ function allobj(ID){ this.style = document.layers[ID]; } document.all = new Array(); } function init(){ if(isNav4){ end = document.layers.length; for(i=0; i < end; i++){ id = document.layers[i].name; document.all[id] = new allobj(id); } } } function catchEvent(id,event_type,function_name){ if (isNav4){ event_to_capture = "Event." + event_type.toUpperCase(); if(id=="document"){ dom = document; eval("document.captureEvents(" + event_to_capture + ")"); }else{ dom = document.layers[id]; eval("document.layers['" + id + "'].captureEvents(" + event_to_capture + ")"); } } else if(isIE4){ if(id == "document"){ dom = document; }else{ dom = document.all[id]; } } temp_event = "on" + event_type.toLowerCase(); dom[temp_event] = eval(function_name); } function letgoEvent(id,event_type,function_name){ if (isNav4){ dom = document.layers[id]; } else if(isIE4){ dom = document.all[id]; } temp_event = "on" + event_type.toLowerCase(); dom[temp_event] = null; }


Now we are going to add some functions to our dom.js file that together will help us enable click and drag. Open up your dom.js file from lesson 15. Once you have it open add the colored text below (last half starting at the line that has "var obj;".

In HTML mode, Type the following code into the CodeRunner below:
nav = document.layers; IE4 = document.all; isNav4 = (nav) ? true : false; isIE4 = (IE4) ? true : false; if(isNav4){ function allobj(ID){ this.style = document.layers[ID]; } document.all = new Array(); } function init(){ if(isNav4){ end = document.layers.length; for(i=0; i < end; i++){ id = document.layers[i].name; document.all[id] = new allobj(id); } } } function catchEvent(id,event_type,function_name){ if (isNav4){ event_to_capture = "Event." + event_type.toUpperCase(); if(id=="document"){ dom = document; eval("document.captureEvents(" + event_to_capture + ")"); }else{ dom = document.layers[id]; eval("document.layers['" + id + "'].captureEvents(" + event_to_capture + ")"); } } else if(isIE4){ if(id == "document"){ dom = document; }else{ dom = document.all[id]; } } temp_event = "on" + event_type.toLowerCase(); dom[temp_event] = eval(function_name); } function letgoEvent(id,event_type,function_name){ if (isNav4){ dom = document.layers[id]; } else if(isIE4){ dom = document.all[id]; } temp_event = "on" + event_type.toLowerCase(); dom[temp_event] = null; } var obj; var canmove=false; function dragenable(id){ catchEvent(id,"mousedown","mousestart"); catchEvent("document","mousemove","mousemoving"); catchEvent("document","mouseup","mouseend"); } function mousestart(e) { canmove=true; if (isNav4) { place = e.target.name.indexOf("1"); layername = e.target.name.substring(0,place); obj = document.layers[layername]; X=e.x; Y=e.y; return false; }else { obj = event.srcElement.parentElement.style; X=event.offsetX; Y=event.offsetY; } } function mousemoving(e) { if (obj && canmove) { if (isNav4) { obj.moveTo((e.pageX - X), (e.pageY - Y)); } else { obj.pixelLeft = event.clientX-X + document.body.scrollLeft; obj.pixelTop = event.clientY-Y + document.body.scrollTop; return false; } } } function mouseend(e) { canmove = false; obj = null; }


Save this as eventdom.js

Before we discuss how this script works, let's see how to use it and get it working. So after you've saved the script above, go ahead and erase what you have in CodeRunner and then copy the following HTML into CodeRunner:

In HTML mode, Type the following code into the CodeRunner below:
<html> <head> <script src=eventdom.js></script> </head> <body onload=setTimeout('init()',600);> <div id=logo style="position:relative;"> <img src=http://useractive.com/logo.gif name=logo1> </div> </body> <script> setTimeout("dragenable('logo')",600); </script> </html>


Go ahead and preview this and see that you can click and drag on the useractive image.

As you can see, to use this library you need to first include it as a script souce. Then you need to put an image inside of a stylesheet. Finally, you need call the the function dragenable and pass the name of the object you want to drag enable.

Be aware that you need to make the image name and stylesheet id the same except that the id of the image should have a 1 at the end.

That's it. that's how it works. Next we'll go over how it works.

How it works

The part of dhtml/javascript that allows us to accomplish Clicking and dragging are the three events: onmousedown, onmousemove and onmouseup. When we activate onmousedown we make the object stick to the cursor by allowing the onmouxemove function to continuously call a function that moves the object as our mouse (cursor) moves. Finally, when you let your mouse up you need to cancel the onmousemove event so that the object quits sticking to your cursor.

We created four functions to enable Click and Drag. They are in order: dragenable, mousestart, mousemoving, and mouseend. Let's take a look at each function and see what's going on.

Observe dragenable:
var obj; var canmove=false; function dragenable(id){ catchEvent(id,"mousedown","mousestart"); catchEvent("document","mousemove","mousemoving"); catchEvent("document","mouseup","mouseend"); }


dragenable is the function that enables whatever object you'd like to be able to click and drag. It simple uses the catchEvent function (see lesson 15) to capture the three events and apply the other functions. The reason we capture the events on the document for mousemove and mouseup is that capturing the events on the document is more stable than on the other objects. This has to do with the more advanced topic of event bubbling.


Observe mousestart:
function mousestart(e) { canmove=true; if (isNav4) { place = e.target.name.indexOf("1"); layername = e.target.name.substring(0,place); obj = document.layers[layername]; X=e.x; Y=e.y; return false; }else { obj = event.srcElement.parentElement.style; X=event.offsetX; Y=event.offsetY; } }


mousestart sets the flags and variables that will allow the next function mousemoving to do it's thing. When we hold the mouse button down this function is called. First thing it does is set the global variable canmove to true which will be used in the mousemoving function. The second job of this function is to set the oject that is capturing the event and recording the coordinates of the cursor relative to the upper left corner of the object.

Of course, we have to have two seperate peices of code for the different types of browsers. If it is Netscape we have: place = e.target.name.indexOf("1"); layername = e.target.name.substring(0,place);
Which gets the object name from the event objects target property. We have to strip off the "1" so the name is right. The reason for this is that the target will actually be the image name, but the object we want to move is it's parent object. Since Netscape doesn't give us the ability to get the parent name, we have to use this naming trick and strip off the "1".

In the next part: obj = document.layers[layername]; X=e.x; Y=e.y;
We set the obj glogal variable to the object that we want to move. and then we record the coordinates where the mousedown event happened. The reason for this will become apparent when we discuss the next function.

If the browser is IE, then we have: obj = event.srcElement.parentElement.style; X=event.offsetX; Y=event.offsetY;
Which is doing the same thing, exept that IE allows us to get the parent element of the target of the event.



Observe mousemoving:
function mousemoving(e) { if (obj && canmove) { if (isNav4) { obj.moveTo((e.pageX - X), (e.pageY - Y)); } else { obj.pixelLeft = event.clientX-X + document.body.scrollLeft; obj.pixelTop = event.clientY-Y + document.body.scrollTop; return false; } } }


mousemoving is the function that does all moving as we move the mouse cursor. First we make sure the the obj and canmove variables are true since we don't want to move the object unless the mouse button is down. Once again Netscape and IE handle the moving differently so we have to split it up into an if-else statement.

If the browser is Netscape we have obj.moveTo((e.pageX - X), (e.pageY - Y)); We use the moveTo function to place the object at the new cursor coordinates which are given by e.pageX - X and e.pageY - Y. The reason for this can be seen in the following diagram:

When we have our mouse down then X and Y are measured relative to the box of the object (the length of the green lines above); whereas pageX and pageY are measured relative to the web page (the length of the aqua lines). This makes pageX - X and pageX - X the coordinates of the upper left hand corner of the objects box, which is the coordinates of the object itself. What we are calculating is called the offset.

Similarly, we for IE we have to calculate the offset but the syntax is different. Also there is a difference conceptually between pageX in Netscape and clientX in IE. pageX and pageY are the coordinates relative to the upperleft hand corner of the document itself so if we scroll the web page the coordinates for an object stay the same, but clientX and clientY are coordinates relative to the window, so if we scroll the web page the coordinates of the object change. The amount of this change is given by document.body.scrollLeft and document.body.scrollTop. So we have to add those in for IE.



Observe mouseend:
function mouseend(e) { canmove = false; obj = null; }


mouseend get's called when we let our mouse up. This function simply resets canmove and obj so that when we move the cursor no object moves anymore.