12 min read

Scripting DragDrop

There are two ways that we could go about achieving our objective. We could take the easy way and treat each draggable object as an independent module, with its own independent properties and event handlers. This would make the code simpler, but would mean that we would require a good deal more of it.

If there are just one or two objects on your page which can be dragged and dropped then this way is fine. However, when you begin to have more than just a couple of objects that can be moved, the amount of code required to handle these objects efficiently increases dramatically.

The second way may be a little more complex and therefore will require a greater degree of understanding. However, this way allows for sharing properties and event handlers across similar drag-and-drop objects. This reduces the overall footprint of your application and saves us an incredible amount of typing.

Creating Individual Drag Objects

We can look at both methods, so you can see just how much work the second way saves us. We’ll start with the easy method. Add the following <script> tag directly before the closing </body> tag:

<script type="text/javascript">
//create the namespace object for this example
YAHOO.namespace("yuibook.dd");
//define the setDDs function
YAHOO.yuibook.dd.setDDs = function() {
//detect the useragent
var ua = YAHOO.env.ua;
if (ua.ie != 0) {
ua.data = "ie";
}
//define variables
var basketTotal = 0;
var basketTot = 0;
var Dom = YAHOO.util.Dom;
//create the 3 DragDropProxy objects
var dd1 = new YAHOO.util.DDProxy("prod1");
dd1.isTarget = false;
dd1.scroll = false;
var dd2 = new YAHOO.util.DDProxy("prod2");
dd2.isTarget = false;
dd2.scroll = false;
var dd3 = new YAHOO.util.DDProxy("prod3");
dd3.isTarget = false;
dd3.scroll = false;
var basket = new YAHOO.util.DDTarget("basket");
//define function to call when dd1 starts being

dragged
dd1.startDrag = function() {
//set cursor position to top-left of proxy
dd1.setDelta(0,0);
}
//define function to call when dd1 stops being

dragded
dd1.endDrag = function() {
}
//define function to call when dd1 enters basket
dd1.onDragEnter = function(e, id) {
//has dd1 been dragged into basket?
if (id == "basket") {
//add 1 to the total number of items
basketTotal += 1;
var tot = YAHOO.util.Dom.get("basketTotal")
tot.innerHTML = basketTotal;
//create new p element to hold product summary
var p = document.createElement("p");
Dom.addClass(p, "prod");
p.id = "prod" + basketTotal;
//add icon for product 1 to basket
var ico = document.createElement("img");
ico.setAttribute("src", "images/prod1_ico.jpg");
ico.id = "imageico" + basketTotal;
Dom.addClass(ico, "icon");
//add product 1 summary to basket
p.appendChild(ico);
Dom.get("basketBody").appendChild(p);
//create new p element for product 1 title
var p2 = document.createElement("p");
var info = Dom.get("prod1info");
//is the browser IE?
if (ua.data == "ie") {
var infos = info.innerHTML.split("<BR>");
} else {
var infos = info.innerHTML.split("<br>");
}
//set product 1 title
var title = document.createTextNode(infos[0]);
Dom.addClass(p2, "prodTitle");
p2.id = "prodTitle" + basketTotal;
//add title to basket
p2.appendChild(title);
Dom.insertAfter(p2, Dom.get(ico));
//create p element for product 1 price
var p3 = document.createElement("p");
var price = document.createTextNode(infos[1]);
Dom.addClass(p3, "prodPrice");
p3.id = "prodPrice" + basketTotal;
//add product 1 price to basket
p3.appendChild(price);
Dom.insertAfter(p3, Dom.get(p2));
//update total basket price
price = Dom.get(p3).innerHTML;
var rawPrice = price.slice(1,6);
var cost = parseFloat(rawPrice);
basketTot += cost;
var newBasketTot = Dom.get("basketCost");
newBasketTot.innerHTML = basketTot;
}
}
//define function to call when dd2 starts being

dragged
dd2.startDrag = function() {
dd2.setDelta(0,0);
}
//define function to call when dd2 stops being

dragged
dd2.endDrag = function() {
}
//define function to call when dd2 enters basket
dd2.onDragEnter = function(e, id) {
if (id == "basket") {
//add 1 to the total number of items
basketTotal += 1;
var tot = YAHOO.util.Dom.get("basketTotal")
tot.innerHTML = basketTotal;
//create new p element to hold product summary
var p = document.createElement("p");
Dom.addClass(p, "prod");
p.id = "prod" + basketTotal;
//add icon for product 2 to basket
var ico = document.createElement("img");
ico.setAttribute("src", "images/prod2_ico.jpg");
ico.id = "imageico" + basketTotal;
Dom.addClass(ico, "icon");
//add product 2 summary to basket
p.appendChild(ico);
Dom.get("basketBody").appendChild(p);
//create new p element for product 2 title
var p2 = document.createElement("p");
var info = Dom.get("prod2info");
//is the browser IE?
if (ua.data == "ie") {
var infos = info.innerHTML.split("<BR>");
} else {
var infos = info.innerHTML.split("<br>");
}
//set product 2 title
var title = document.createTextNode(infos[0]);
Dom.addClass(p2, "prodTitle");
p2.id = "prodTitle" + basketTotal;
//add title to basket
p2.appendChild(title);
Dom.insertAfter(p2, Dom.get(ico));
//create p element for product 2 price
var p3 = document.createElement("p");
var price = document.createTextNode(infos[1]);
Dom.addClass(p3, "prodPrice");
p3.id = "prodPrice" + basketTotal;
//add product 2 price to basket
p3.appendChild(price);
Dom.insertAfter(p3, Dom.get(p2));
//update total basket price
price = Dom.get(p3).innerHTML;
var rawPrice = price.slice(1,6);
var cost = parseFloat(rawPrice);
basketTot += cost;
var newBasketTot = Dom.get("basketCost");
newBasketTot.innerHTML = basketTot;
}
}
//define function to call when dd3 starts being

dragged
dd3.startDrag = function() {
dd3.setDelta(0,0);
}
//define function to call when dd3 stops being

dragged
dd3.endDrag = function() {
}
//define function for when dd3 enters basket
dd3.onDragEnter = function(e, id) {
if (id == "basket") {
//add 1 to the total number of items
basketTotal += 1;
var tot = YAHOO.util.Dom.get("basketTotal")
tot.innerHTML = basketTotal;
//create new p element to hold product summary
var p = document.createElement("p");
Dom.addClass(p, "prod");
p.id = "prod" + basketTotal;
//add icon for product 3 to basket
var ico = document.createElement("img");
ico.setAttribute("src", "images/prod3_ico.jpg");
ico.id = "imageico" + basketTotal;
Dom.addClass(ico, "icon");
//add product 3 summary to basket
p.appendChild(ico);
Dom.get("basketBody").appendChild(p);
//create new p element for product 3 title
var p2 = document.createElement("p");
var info = Dom.get("prod3info");
//is the browser IE?
if (ua.data == "ie") {
var infos = info.innerHTML.split("<BR>");
} else {
var infos = info.innerHTML.split("<br>");
}
//set product 3 title
var title = document.createTextNode(infos[0]);
Dom.addClass(p2, "prodTitle");
p2.id = "prodTitle" + basketTotal;
//add title to basket
p2.appendChild(title);
Dom.insertAfter(p2, Dom.get(ico));
//create p element for product 3 price
var p3 = document.createElement("p");
var price = document.createTextNode(infos[1]);
Dom.addClass(p3, "prodPrice");
p3.id = "prodPrice" + basketTotal;
//add product 3 price to basket
p3.appendChild(price);
Dom.insertAfter(p3, Dom.get(p2));
//update total basket price
price = Dom.get(p3).innerHTML;
var rawPrice = price.slice(1,6);
var cost = parseFloat(rawPrice);
basketTot += cost;
var newBasketTot = Dom.get("basketCost");
newBasketTot.innerHTML = basketTot;
}
}
}
//execute setDDs when DOM is ready
YAHOO.util.Event.onDOMReady

(YAHOO.yuibook.dd.setDDs);
</script>

All of the code here sits within the setDDs() function, which is called using the Event utility’s onDOMReady() method.

The first section of code within the setDDs() function uses the .env.ua() method of the YAHOO global object to determine the user-agent string of the browsing environment. We can use this as a quick and easy way of detecting the browser being used to view the page.

The ie property exists within the ua object even if IE is not in use, but if it isn’t in use, the property is set to 0. Therefore, if the ie property does not equal to 0 it means that IE is the browser currently being used. In this case, the ie property will hold an integer representing the version of IE in use. When IE is being used, we set the data property of our ua variable to the string ie.

We then create a series of variables for use in the script. The last variable is created purely for convenience. We’ll be making heavy use of the DOM utility during this example, so defining that first part of the DOM call as a short variable helps make things easier on us.

Next we create all three of the individual DDProxy objects. Remember, in this implementation, each object has to have its own properties and event handlers defined for it. We also define the basket as a DDTarget so that we can make use of the .onDragEnter() method.

Because we want each product to interact only with our shopping basket and not the other products on the page, we have to set the .isTarget property to false. As well as being able to specifically create drop targets using the YAHOO.util.DDTarget class, any drag object is by default also a drop target. Additionally, we can switch off the .scroll property, which causes the viewport to scroll indefinitely when the dragged object exceeds the window boundary.

Using DragDrop Events

Three event handlers are required for this example: startDrag, endDrag, and onDragEnter. The startDrag event fires as soon as the left mouse button has been held down for the required length of time on a drag object, or the pointer moves the specified number of pixels.

The .setDelta() method used in the startDrag event handler allows us to control where the pointer is relative to the drag object, or in this case, the proxy element. By specifying 0,0 as arguments for this method, we are instructing the pointer to appear at the top-left corner of the proxy element. This is needed so that the element is placed back where it began instead of where the pointer was relative to the drag object when the drag began.

The endDrag event fires when a mouseup event is detected on the object being dragged. The anonymous function here has a very special purpose, even though it is just an empty function.What it does is ensure that the item being dragged is returned to its original position once it has been dropped instead of remaining where it was dropped. This is important because if we didn’t keep the listing pictures in their proper locations, the page would soon be littered with abandoned drag objects.

The onDragEnter event fires when the moving object is dragged over a legal target. Since the shopping basket is the only valid target on the page, this will fire whenever a product object is dragged over the basket. This function is where the bulk of our code lies.

Two arguments are specified for this function. The first is the event object, which is automatically passed to our handler. The second argument is the id of the element that triggered the event, which in this example will be the basket.

We first use our YAHOO.util.Dom shortcut to get the <span> element displaying the number of items in the basket, then use the innerHTML property to alter this value in accordance with the basketTotal variable.

We create a new paragraph element and give it a class of prod. This new element will act as a container for a short summary of the item placed in the basket. We can go ahead and create the different items that will make up this short description of the product that has been purchased.

A new <img> element is created and stored in the ico variable. Its src attribute is set to the icon representing the product. We then give it a class name so that we can style it and an id attribute so that we can identify it. Once created and configured, we can append the <img> to the new <p> element, and then append the <p> element to the body of the basket. Now let’s work on extracting some of the listing description to display in the basket.

We create another new <p> element and give it a class of prodTitle. We then need to do more detection to determine which product was dropped, getting the full listing text of the product in the title variable once we have.

Each part of the listing text is separated by a <br> element, so we can create an array of each different bit of information contained in the listing text using the .split() method. This is the part of the script where we use the information gathered from the ua property earlier on.

IE for some reason capitalizes all HTML elements in the DOM. This means that IE sees <BR> tags, while other browsers see <br>, and is the reason we need the if statement to determine the browser in use.

The first item in the infos array is the name of the product, so we can use this as the title for the product summary and create a new textNode based on it. This title element is then inserted into the DOM directly after the icon element using the DOM utility’s .insertAfter() method, which takes the element to insert and the element it should be inserted after as arguments.

For good measure, let’s add the price of the product to the product summary. A final

element is created to hold a new textNode comprised of the second item in the infos array, which happens to be the price of the product. The class for the new <p> element is set and it is then inserted as a sibling of the previous element that was created.

At the bottom of our basket is a price indicator showing the total cost of all items in the basket; we can easily update this using the same method as we updated the basket contents total earlier on. We recycle the price variable, updating it with the HTML element from the basket.

As the price is currently a text string rather than a number, we have to convert it. Before we can do this however, we need to remove the currency symbol from the front of it, which we can do with the .slice() method.

Then we can use the standard JavaScript parseFloat math function to convert the remaining string into a true floating-point number. We can then update the current cost by adding the cost of the dropped product to it. Finally, we set the innerHTML property of the basketCost span element to the new total.

As each drag object is completely independent, the rest of our script is made up of identical event handlers tied to the other drag objects (products 2 and 3). This is what bloats our code to three times the size it actually needs to be, and imagine how much typing would need to be done if there were 30 products on the page

LEAVE A REPLY

Please enter your comment!
Please enter your name here