Drag-and-Drop with the YUI: Part-1

Packt Publishing

Develop your next generation web applications with the YUI JavaScript development library

Dynamic Drag-and-Drop without the Hassle

Like most of the other library components, when creating your own drag-and-drop elements, there are a range of different options available to you that allow you to tailor those objects to your requirements. These properties, like those of most other library components, can be set using an object literal supplied with the constructor, but in most cases even this is not required.

The most challenging aspects of any drag-and-drop scenario in your web applications are going to center around the design of your specific implementation rather than in getting drag-and-drop to work in the first place. This utility is yet another example of the huge benefits the YUI can provide in reducing the amount of coding and troubleshooting that you need to concern yourself with.

The Different Components of Drag-and-Drop

In addition to the configurable properties used in your object literal, you also have several constructors that can be used to enable drag-and-drop. The first constructor YAHOO.util.DD allows for drag-and-drop at its most basic level. The supplied element will be transformed into an object that can be dragged around the page.

The mechanics of drag-and-drop result in a burden of fairly high processing. The library has to keep track of the mouse pointer whilst it is moving, the draggable object needs to be repositioned, and different events are almost continually firing while the drag is taking place.

In order to minimize the amount of information that needs to be processed, especially when the draggable object is fairly large, you can make use of a proxy element that will track across the page with the mouse pointer. When the proxy element reaches its final destination, it disappears and is replaced by the actual element.

If a proxy element is required, we can use the YAHOO.util.DDProxy constructor instead of the basic constructor. As the proxy element is just an empty <div>, it's much easier to track and can even be shared between different drag objects on the page, reducing the overall processing that's required.

Personally I think the default appearance of the proxy element is perfectly adequate, however you can also create your own custom elements to use as a proxy. The figure below shows the default proxy-element appearance:

Design Considerations

When working with DragDrop implementations, it is useful to consider the following aspects of the design:

Can any part of the drag object be clicked on to initiate the drag, or should a drag handle be defined?

Can the object be dropped on to any part of the page or should a specific drop target be defined?

Should anything occur whilst the object is being dragged?

Should anything occur when the item is dropped on a non-valid target?

Should anything occur when the object is dropped on to a valid target?

Events

Events are an integral aspect of many DragDrop situations. Sometimes, however, being able to move something around the screen is the only behavior that's required, but generally you'll want something to happen either while the object is being dragged, or when it is dropped.

The YAHOO.util.DragDrop utility provides a series of custom events which allow you to hook into and respond to events such as startDrag, endDrag, onDrag, onDragDrop, and onInvalidDrop. Valid targets also expose events of their own including onDragEnter, onDragOut, and onDragOver. We will be looking at a number of these events during the example that follows.

Allowing Your Visitors to Drag-and-Drop

The Drag-and-Drop utility uses tried and tested DHTML techniques, as well as some innovative new features, to allow you to easily create objects that can be dragged and then dropped. All that you need to do to make an element on your page dragable is to create a new instance of the YAHOO.util.DD class and feed in the id or element reference of the element that drag is to be enabled for.

DragDrop Classes

The base class of the Drag-and-Drop utility is YAHOO.util. DragDrop, but you'll use one of its extending subclasses, like YAHOO.util.DD, most of the time. This subclass inherits all of the properties and methods of the base class and even adds a few of its own, so it's more than capable of handling most of your drag-and-drop requirements.

The YAHOO.util.DD class also has its own subclasses to deal with additional drag-and-drop requirements for different situations. The subclasses are YAHOO.util.DDProxy and YAHOO.widget.SliderThumb. As you can see, the Drag-and-Drop utility provides some of the basic functionality of another of the controls found in the YUI, the Slider Control (which we will look at in more detail towards the end of this article). Let's examine the DDProxy class, as that is relevant specifically to the Drag-and-Drop utility.

Creating a proxy object that tracks with the cursor when a drag object is being dragged instead of allowing the actual drag object to track can prevent problems that arise with large drag objects not tracking properly or obscuring other content on the page. The proxy object is a small, empty object that represents the drag object and shows only its borders.

The proxy object is created on the mouseDown event of the drag object and the actual drag object does not move to its new position until the mouseUp event is fired. Using a proxy object is both visually appealing and better overall for all but the simplest of implementations performance wise.

The Constructor

The constructor for an instance of the DragDrop object can also take a second or third argument when you are instantiating objects for dragging. The second argument, which is optional, specifies the group to which the element being dragged belongs.

This refers to interaction groups—the object being dragged can only interact with and fire events with other elements in its interaction group. The third argument, which is also optional, can be used to supply a configuration object, the members of which hold additional optional configuration properties that can easily be accessed and set.

Every object instantiated with the drag-and-drop constructor is a member of one or more interaction group(s), even if the second argument is not passed. When the argument is not supplied, the object will simply belong to the 'default' group instead. There is no limit as to how many groups an object can belong to.

The API provides just two methods that relate to group access. .addToGroup() is used to add the object to more than one group, so the first group membership is defined with the constructor and subsequent groups with the .addToGroup() method. To remove an object from a group, just call the .removeFromGroup() method.

Target Practice

There is no doubt that drag-and-drop adds a hands-on, fun element to surfing the net that is way more engaging than simple point-and-click scenarios, and there are many serious applications of this behavior too.

But dragging is only half of the action; without assigned drop targets, the usefulness of being able to drag elements on the page around at leisure is almost wasted.

Drop targets have a class of their own in the Drag-and-Drop utility. Which extends the YAHOO.util.DragDrop base class to cater for the creation of drag elements that aren't actually dragable and this is the defining attribute of a drop target.

The constructor is exactly the same as for the DD and Proxy classes with regard to the arguments passed, but YAHOO.util.DDTarget is used instead. The Target class has no methods or properties of its own, but it inherits all of the same methods and properties as the other two classes, including all of the events.

Get a Handle on Things

By default, holding down the mouse button whilst hovering over a drag object results in the mouse pointer 'picking up' the drag object. The pointer can be over any part of the drag object and the drag action will still be initiated.

Handles change this default behavior and become the only part of the drag object that responds to the mouseDown event. You can define multiple handles on a single drag object and the handle can even be completely external to the drag object.

To use a handle (or handles) for dragging, the draggable element itself is defined in the same way, but a child element is specified in the underlying HTML as the handle. It is then created by the library by calling the .setHandleElld(), or the .setOuterHandleElld() methods.

Both of these methods take just one parameter—the id of the element to use as the handle. The first method is used for handles that appear within the boundaries of the drag object, the second method is used when the handle appears outside thedrag object.

There are several other useful methods that revolve mostly around allowing you to specify child elements of drag handles that should not initiate a drag interaction, or for indicating a CSS class or HTML element type that should not act as a drag handle or react to mouseDown events. This is useful for creating elements within the drag-and-drop element that react to clicks in a different way.

The Drag-and-Drop Manager

The page-wide manager of all drag-and-drop interactions isYAHOO.util.DragDropMgr. It features a huge API stuffed to the brim with properties and methods (although a number of the methods are marked as deprecated)

There's a lot in this class but fortunately, you don't need to take much notice of it as it works in the background during drag interactions to make sure everything proceeds as it should. There are a few helper methods that can be hugely useful however, so it is worth taking at least a quick look at the source file to see what they are.

The getBestMatch() function is useful for when a drag object overlaps several drop targets and helps you decide which target the element should actually be dropped on. This method is only used in intersect mode (see the next section) and takes an array of the targets as its argument. It returns the best match based on either the target that the cursor is over or the element with the greatest amount of overlap.

There are also a couple of properties that are used to initiate the dragging of an object. The first property is the pixel threshold (clickPixelThresh) which tells the script how many pixels the cursor should move while the mouse button is held down before a drag begins, the default is three pixels.

The second property is the time threshold (clickTimeThresh) which defines how long the button needs to be held down whilst the pointer is stationary before it is recognized as a drag. The default for this one is 1000 milliseconds.

Interaction Modes

The utility supports two types of interaction: point mode and intersect mode (although there are actually two types of intersect). Point mode means that as soon as any point of the mouse cursor touches any point of the drop target, the dragOver event fires. Alternatively, in intersect mode, as soon as the drop target is touched at any point with the drag object (even by just 1 pixel), the dragOver event fires.

To switch between the two modes programmatically, the DragDropManager has a property called mode (YAHOO.util.DDM.mode), which can be set to one of three values: YAHOO.util.DDM.POINT, YAHOO.util.DDM.INTERSECT or YAHOO.util.DDM.STRICT_INTERSECT.

With intersect mode, the interaction is defined by either the cursor or the amount of overlap, but with strict intersect the interaction is defined just by the amount of overlap. POINT mode is the default value.

Implementing Drag-and-Drop

To highlight some of the basic features and considerations of drag-and-drop we'll create a shopping basket application, which can be used by visitors to drag products they want to buy into the basket.

The page that we'll end up with by the end of this example will look like this:

In a blank page in your text editor, begin with the following basic HTML page:

We'll use the doc3 100% fluid page layout and the t6 template to give us a convenient right-hand side bar into which we can place our shopping basket. We can also add the link for the external stylesheet that we'll create shortly.

Let's add the code for the products listing first. Normally, this kind of data would come out of a database dynamically when the page was loaded, but in this example, the data will be will be hard-coded into the page. We are inputting the data this way so that we can focus purely on the drag-and-drop aspect.

To keep things simple, we'll just show a couple of products; insert the following code into the yui-g div:

We can add a few of the things that you'd expect to find on a page such as this, like the breadcrumb trail links and more links. Clicking on these obviously won't do anything in our example, but it helps set the overall effect of the page

Now let's add the mark up for the shopping basket itself. Add the below code to the yui-b container <div>:

Save what we've added so far as dragdropbasket.html in your yuisite folder.

The Required CSS

As drag–and-drop itself is behavior-based rather than a UI control, we don't need to worry about using sam skin files. However, in order to set the page up correctly, we will be making use of the grids CSS tool, as well as our own custom stylesheet.

At this stage, there's nothing particularly spectacular about the collection of elements that make up the basket. In order for both aspects of the page to look right, we also need to add some basic CSS. In a new file in your text editor, add the following CSS code:

This is it for now as far as the CSS goes. We'll need some more in a little while, but for now save this file as basket.css in the same directory as the HTML page.

If you re-open the dragdropbasket.html file in your text editor again, we can begin adding the JavaScript that will bring this page to life. At this point, your page should now resemble the screenshot at the start of this section.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.