Native Drag-and-Drop Support


Letting users drag and drop interface elements is something we’ve been able to do with JavaScript libraries for quite a while, but the W3C has adopted Microsoft’s Drag and Drop implementation as part of the HTML5 specification.7 It’s supported by Firefox, Safari, Internet Explorer, and Chrome, but in actuality it’s a mess.

The implementation at first appears to be straightforward; we designate an element as “draggable,” we then designate an element that watches for a dropped object, and we execute some code when that happens.

In reality, it’s not nearly that simple. To demonstrate, let’s create a simple drag-and-drop interface that lets us drag small images into a drop area that will load the larger version.

<div id=”images”>
<i mg s rc=”images/red_thumb.jpg”
d a t a – l a r g e = “images/red.jpg” alt=”/A red flower”>
<i mg s rc=”images/purp1e_thumb.jpg”
data-~arge=”images/purple .jpg” alt=”/A white and purple flower”>
<i mg src=”images/white_thumb.jpg”
data-~arge=”images/white.jpg” alt=”/A white flower”>
< / d i v >
<div id=”preview”>
<p>Drop images here</p>
< / d i v >

We’re using custom data attributes here to hold the source of the larger version of our photos.

Our photo viewer

Next we’ll add some basic styles to float the two columns:

#images img{
-webki t – u s e r – d r a g
f l o a t :
w i d t h :
margi n
# p r e v i ew{
f l o a t : l e f t ;
w i d t h : 500px;
b a c k g r o u n d – c o l o r : #ddd;
h e i g h t : 335px;
. h o v e r {
b o r d e r : lOpx s o l i d #000;
b a c k g r o u n d – c o l o r : #bbb ¡ i m p o r t a n t ;

At this point, our Interface looks like the one In Figure 11.1, on the previous page. Now let’s add some events so we can drag the photos.

Drag-and-Drop Events

We’ll need to work with several events related to dragging and dropping elements.

Drag-and-Drop EventsThat’s a total of seven events just to handle dragging and dropping elements, and some of the events have default behaviors. If we don’t override them, the whole thing fails.

First, we need to define all of our list items as draggable.

var c o n t a c t s = $(‘#images i m g ‘ ) ;
c o n t a c t s . a t t r C ‘ d r a g g a b l e ‘ , ‘ t r u e ‘ ) ;

We’re adding the draggable HTML5 attribute. We could do this in our markup, but since we require JavaScript to do the interaction, we’ll apply this attribute with our script.

When we drag the image, we want to grab the address of the large Image and store it. We’ll bind to the ondragstart event, and to keep it simple and cross-platform, we’ll use jQuery’s bind() method.

Une l c o n t a c t s . b i n d ( ‘ d r a g s t a r t ‘ , f u n c t i o n ( e v e n t ) {
2 var data = e v e n t . o r i g i n a l E v e n t . d a t a T r a n s f e r ;
3 var src = $ ( t h i s ) . a t t r ( ” d a t a – 7 a r g e ” ) ;
4 data.setDataC”Text”, s r c ) ;
5 return t r u e ;
6 });

The specification provides a datastorage mechanism that lets us specify the type of data and the data itself, which is passed along as part of the event. jQuery’s bind() method wraps the event in its own object, so we use the originalevent property on 2 to access the real event. We store theURL to the image on the event by using the setData() method on line 4, using Text as the data type.

Now that we can drag elements, let’s talk about how we fire events when the user drops the elements.

Dropping Elements

We want our “To” form field to act as our drop target, so we’ll locate it and bind the drop event.

Une l var t a r g e t = $ ( ‘ # p r e v i e w ‘ ) ;
t a r g e t . b i n d C ‘ d r o p ‘ , f u n c t i o n ( e v e n t ) {
v a r data = e v e n t . o r i g i n a l E v e n t . d a t a T r a n s f e r ;
5 var src = ( d a t a . g e t D a t a ( ‘ T e x t ‘ ) );
v a r img = $ ( ” < i m g x / i m g > ” ) . a t t r ( ” s r c ” , s r c ) ;
$ ( t h i s) . h t m l (img) ;
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
io r e t u r n ( f a l se) ;
} ) ;

We retrieve the image address we passed with the event using the get- Data () method on line 5, and we then create a new image element that we push into our content region.

We need to cancel the default ondrop event so it won’t fire when our user drops the element onto the target. To do that, we need to use both preventdefault() and return false. Internet Explorer needs return false, and other browsers need preventDefault().

If we try to use this in Chrome or Safari right now, it won’t work quite right. At a minimum, we have to override the ondragover element. If we don’t, our ondrag event won’t respond. So, we’ll do that by using this code:

t a r g e t . b i n d C ‘ d r a g o v e r ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

We’re just canceling out the default event again the same way we did with the ondrop event. Let’s do the same with the ondragend event too.

c o n t a c t s . b i n d C ‘ d r a g e n d ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

This will cancel out any browser events that fire when our user stops dragging an element, but it won’t interfere with our defined ondrop event.

Changing Styles

We want to let the user know they have dragged an element over a drop target, and we can do that using the ondragenter and ondragleave methods.

c o n t a c t s . b i n d C ‘ d r a g e n d ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

This applies our hover class in our style sheet, which will be applied and removed when these events fire.

File Dragging

Moving text and elements around the page is just the beginning. The specification allows developers to create interfaces that can receive files from the user’s computer. Uploading a photo or attaching a file is as easy as dragging the file onto a specified target. In fact, Google’s Gmail supports this if you are using Firefox 3.6 or Chrome 5. If you want to explore this further, take a look at the excellent article9 by Leslie Michael Orchard.

All Is Not Well

The behavior in various browsers is, to be kind, inconsistent. IE 8 works, but it breaks if we try to set the data type for setData() to Url instead of Text.

Additionally, in order to support dragging of elements that are not images or links in Safari 4, we’d need to add additional CSS to our style sheet.

# c o n t e n t s l i {
-webki t – u s e r – d r a g

Throughout this book, we’ve discussed how important it is to keep style and behavior separated from content, and this flies right in the face of that concept.

Don’t try dragging text onto form fields. Modern browsers already let you do this, but there’s no good way to override that behavior.

As it stands, we can get much better results with much less code by using a JavaScript library that supports dragging and dropping like jQuery UI

Even with a library, we still have one last thing to worry about: accessibility. The specification doesn’t say anything about how to handle users who can’t use a mouse. If we implemented drag-and-drop functionality on our interfaces, we’d need to develop a secondary method that didn’t require JavaScript or a mouse to work, and that method would depend on what we’re trying to do.

This specification has a lot of potential, but it also has some things that need to be addressed. Use it if it makes sense, but ensure you don’t force your users into something they can’t use.