Free Saeed Malekpour, Website Developer

Contact Me

@zoltandulac

Most Popular Posts

  • Cross Browser CSS Transforms – even in IE
  • How to Detect Font-Smoothing Using JavaScript
  • @font-face in Depth
  • Cross Browser HTML5 Drag and Drop
  • Installing Cygwin and FontForge for Windows
  • Cross Browser HTML5 Progress Bars In Depth
  • Creating Cross Browser HTML5 Forms Now, Using modernizr, webforms2 and html5Forms
  • Categories

    Cross Browser HTML5 Drag and Drop

    January 10th, 2010 by zoltan · 68 Comments

    Updates:

    • May 10, 2013: This article has been translated into Serbo-Croatian language by Anja Skrba from Webhostinggeeks.com.
    • Feb 3, 2009: A bug in Webkit seems to be the culprit in the permissions form example below not working correctly in Safari 4. The code has been updated to work around this bug and the article below has been updated. Thanks to russbuelt for pointing this out.
    • Feb 3, 2009: Apparently, an example made by Apple which I reported not working in Safari does work in Safari 4.0.4 for Mac OS X. Thanks for Scott Straker for this information.

    Remixed version of image by Svartling.

    Image Credit: Flickr user svartling

    HTML5 Drag and Drop has been talked about a lot lately, but it’s hard to find really useful information about implementing it across multiple browsers. Mozilla, Apple and Microsoft all have pages describing how to use it, but their examples seem to work only in their particular browser (Apple’s example doesn’t even work in their own! Updated, Jan. 11, 2009: Although I have not been able to get this example working on Safari 2.0.4 and 3.1.2 for OS X and 4.0.4 for Windows, I have received word that it works on Safari 4.0.4 on OS X). Remy Sharp’s great article Native Drag and Drop was a good place for me to start — however, the examples didn’t work in Internet Explorer. I also thoroughly enjoyed JavaScript guru Peter-Paul Koch’s humorous and lengthy rant about cross-browser drag and drop headaches where he uses creative and colourful language to describe what he thought of the standard, the browser manufacturers, and the WHAT-WG.

    When normal people see the author of the Compatibility Master Tables respond negatively to a web technology, they would probably assume it would be a good sign to stay away from it.

    However, I am not normal. With a name like Zoltan, how could I possibly be normal? (and yes, it is my real name).

    So, I decided to find out for myself how bad HTML5 Drag and Drop really is. Almost immediately, I understood Koch’s reaction – the browser vendors have not implemented all the same features, and there are even a few quirks in how the features that are implemented work. However, after doing a lot of research, I found a common denominator that works well, with the help of a small bit of JavaScript that smooths out the edges. Despite the implementation flaws, Future-proof HTML5 Drag and Drop is not too hard for developers to use in their own applications. This article will explain how to do this step by step with many examples along the way. By the time you are done, you will be able to write useful drag and drop scripts of your own like in this example:

    See an example of HTML5 Drag and Drop in action.

    Advantages Over Existing Drag and Drop Implementations

    Koch mentioned in his blog post that “Web developers MUST NOT (in the sense of RFC 2119) use HTML 5 drag and drop. They should use old-school scripts instead”. I would argue that developers should use HTML5 drag and drop for the following reasons:

    • JavaScript Framework Independent: Most other (but not all) drag and drop implementations are tied into 3rd party frameworks like Dojo, Prototype or jQuery.
    • Built-in Browser Support: HTML5 Drag and Drop is supported in Firefox 3.5+, Chrome 3.0+, Safari 3.0+ and Internet Explorer 5.0 (Note: that is not a typo — HTML5 Drag and Drop is based on work done by Microsoft in 1999). Because it is part of HTML5, I assume Opera support should be inevitable.
    • Integration With Other Web Applications: the HTML5 specification will allow developers to produce drag and drop scripts that work across frames, and across browser windows.
    • Integration With Non-Web Applications: the HTML5 specification also allows users to drag and drop data to and from non-web applications

    The Basics of Drag and Drop, Step by Step

    In order to save other developers from the headaches I got deciphering cross browser drag and drop, I present the following guide that shows how to do in five easy steps. Every step will describe a set of related concepts, and will show examples, most of which are built on code from previous steps. At the end of each step, I will discuss any issues and interesting bits I came across.

    Step 1: Defining a Draggable Object

    First you need to define the HTML nodes that you want to drag. Firefox requires that these nodes have their draggable attribute set to "true", Internet Explorer requires that the node must be an <a> tag with (the href attribute set) or an <img> tag.

    <a href="#" id="toDrag" draggable="true">This is a draggable item</a>

    To show how this works, I have written a very simple page with a the above markup in the <body>:

    <!DOCTYPE html>
    
    <html lang="en">
        <head>
            <meta name="generator" content="HTML Tidy, see www.w3.org">
    
            <title>Test #1: A Simple Draggable Object</title>
    
          <script type="text/javascript" src="../../shared/js/EventHelpers.js">
          </script>
          <script type="text/javascript" src="../../shared/js/DragDropHelpers.js">
          </script>
    
            <link rel="stylesheet" type="text/css" media="screen" href=
            "css/test1.css">
        </head>
    
        <body>
            <h1>Test #1: A Simple Draggable Object</h1>
    
            <a href="#" id="toDrag" draggable="true">This is a
            draggable item</a>
    
            <p>Try to drag the red box around. You will see the
            draggable object cloned in every browser except Explorer
            and Chrome.</p>
    
            <a href=
            "http://www.useragentman.com/blog/2010/01/10/cross-browser-html5-drag-and-drop/">
            Go back to the User Agent Man HTML5 Drag and Drop
            article</a>
        </body>
    </html>
    

    Note that this HTML page has two scripts in the <head>:

    • EventHelpers.js: this script implements cross-browser event handling routines without the need for a JavaScript framework. All the examples in this article use it, but feel free to refactor all the code in this article to use a third party framework like Dojo, jQuery or whatever framework you like.
    • DragDropHelpers.js: among other things that I will get to later, will activate dragging on draggable="true" objects in Safari and Chrome using a built-in copy of Dean Edwards’ cssQuery. It does this by inserting the following CSS code into the HTML document:
      [draggable=true] {
        -khtml-user-drag: element;
        -webkit-user-drag: element;
        -khtml-user-select: none;
        -webkit-user-select: none;
      }

      The first two rules allow the dragging of the draggable="true" nodes, while the last two prevent the user from selecting the text within the node (which can cause some unexpected behavior in Webkit browsers). The -khtml properties work in older versions of Safari, while -webkit ones work in newer versions. Without these CSS properties, these nodes will not be draggable when using the Safari web browser

    See Example #1: a draggable object

    A couple of notes:

    1. You’ll notice that when you hover the mouse pointer over the draggable item, it will change to The  "Move" cursor(i.e. I the “move” cursor). This is not the default behavior of the web browser – it is something I added in DragDropHelpers, since I believe that it’s a nice visual cue to show users that an object is draggable (if you don’t want this behavior, it is possible to turn it off in your own scripts by setting DragDropHelpers.showMouseoverCue to false
    2. How draggable objects look in the various browsers varies. Here are screenshots of how it looks in Windows web browsers (Note: although all screenshots were taken using Windows XP, similar results occur when viewing under Windows Vista or Windows 7):
      Firefox (Windows XP) Safari (Windows XP) Explorer & Chrome (Windows XP)
      [Dragging of a link element, Firefox Windows] [Dragging of a link element, Safari Windows] [Dragging of a link element, Explorer Windows]

      Note the differences between these three browsers:

      • Firefox for Windows shows a cloned version of the draggable object underneath the mouse pointer when the object is being dragged, while Safari displays a grey box with the text “This is a draggable item” written inside. Explorer and Chrome don’t show any effect at all (We will address this issue later).
      • Since we have not established where we are going to drag our object, all Windows browsers will change the mouse pointer cursor to "Not-Allowed" cursor. (a.k.a. the “not-allowed” mouse pointer). This is the expected behavior, and you will see later that it will help the user know where to drop the object.

      On OS X, Firefox and Safari look similar to Firefox for Windows, except that the “not-allowed” mouse-pointer doesn’t show up:

      Firefox (OS X) Safari (OS X) Chrome (OS X)
      [Dragging of a link element, Firefox Windows] [Dragging of a link element, Safari OS X] Chrome for Mac

      The Linux browsers don’t have the “not-allowed” mouse cursor either. Also, the cloned version of the draggable object isn’t transparent using the Linux version of Firefox, and Chrome under Linux behaves pretty much the same as on the other platforms (Note: all Linux testing was done using Ubuntu 9.10 using the standard Gnome window manager).

      Firefox (Ubuntu) Chrome (Ubuntu)
      Firefox Linux Chrome Linux

      The fact that Explorer and Chrome don’t have any visual dragging cues bugged me, so I put a “fix” inside DragDropHelpers.js which can be turned on by setting DragDropHelpers.fixVisualCues to true. Let’s look at a slightly refactored version of Example #1 where we insert the following code in the <head>:

      <!DOCTYPE html>
      <html lang="en">
         <head>
            <title>Test #1: A Simple Draggable Object (with visual cues added to Explorer and Chrome)</title>
            <script type="text/javascript" src="../../shared/js/EventHelpers.js"></script>
            <script type="text/javascript" src="../../shared/js/DragDropHelpers.js"></script>
            <script type="text/javascript">
            <!--
            DragDropHelpers.fixVisualCues=true;
            -->
            </script>
      
            <link rel="stylesheet" type="text/css" media="screen" href="css/test1.css" />
      
         </head>
         <body>
      
            <h1>Test #1: A Simple Draggable Object (with visual cues added to Explorer and Chrome)</h1>
      
            <a href="#" id="toDrag" draggable="true">This is a draggable item</a>
      
            <p>Try to drag the red box around.  You will see the draggable object cloned in all browsers.</p>
      
            <a href="http://www.useragentman.com/blog/2010/01/10/cross-browser-html5-drag-and-drop/">Go back to the User Agent Man HTML5 Drag and Drop article</a>
      
         </body>
      </html>
      

    See Example #1a, a draggable object with visual cue fix for Explorer and Chrome

    Voila! The visual cue now shows up. I’ll explain how this works at the end of the article.

    Step 2: Setting Events on the Draggable Object

    Now that you have defined an object as draggable, you should attach some events to it so the browser knows what to do while it is being dragged. There are three events that a draggable object can fire:

    Event name Description
    dragstart Fires when the user starts dragging of the object.
    drag Fires every time the mouse is moved while the object is being dragged.
    dragend Fires when the user releases the mouse button while dragging an object.

    A developer would attach these events the way they would any other JavaScript event – I use EventHelpers.addEvent, but one could use jQuery’s bind() method, prototype’s Event.observe() or whatever you favourite framework provides for event handling. Developers can also use the old school inline events (i.e. <a href="#" id="toDrag" draggable="true" ondragstart="somefunction();">This is a draggable item</a>), but I try to stay away from using those to seperate document structure from behavior.

    Let’s take the example in step 1 and change it so we can log when these events happen to the toDrag node:

    <!DOCTYPE html>
    <html lang="en">
       <head>
          <title>Example #2: a draggable object with events attached </title>
          <script type="text/javascript" src="../../shared/js/sprintf.js"></script>
          <script type="text/javascript" src="../../shared/js/EventHelpers.js"></script>
          <script type="text/javascript" src="../../shared/js/DragDropHelpers.js"></script>
    
          <script type="text/javascript" src="js/02-dragObjectWithEvent.js"></script>
    
          <link rel="stylesheet" type="text/css" media="screen" href="css/test1.css" />
    
       </head>
       <body>
    
          <h1>Example #2: a draggable object with events attached</h1>
    
          <p>Try to drag the red box around.  This page will tell you if the
          object is currently dragging, and will log all <code>dragstart</code>
          and <code>dragstop</code> events</p>
    
          <p><strong>Is dragging:</strong> <span id="dragEventNotice">no</span>
    
          <p id="eventLog"><strong>Event now firing:</strong><br /><span id="eventNotice"></span>
    
          <a class="goBack" href="http://www.useragentman.com/blog/2010/01/10/cross-browser-html5-drag-and-drop/">Go back to the User Agent Man HTML5 Drag and Drop article</a></p>
    
          <a href="#" id="toDrag" draggable="true">This is a draggable item</a>
    
       </body>
    </html>
    

    In the 02-dragObjectWithEvent.js script, the dragEventNotice node will be used to report whether the user is dragging the object around, and the eventNotice will be used to log what events the user has fired:

    var dragObject = new function () {
       var me = this;
    
       var dragNode;
       var eventNoticeNode, dragEventNoticeNode;
    
       /* runs when the page is loaded */
       me.init = function () {
    
          if (EventHelpers.hasPageLoadHappened(arguments)) {
             return;
          }   
    
          /* The node being dragged */
          dragNode=document.getElementById('toDrag');
    
          /* The nodes that report to the user what is happening to that node*/
          eventNoticeNode = document.getElementById('eventNotice');
          dragEventNoticeNode = document.getElementById('dragEventNotice');
    
          /* The drag event handlers */
          EventHelpers.addEvent(dragNode, 'dragstart', dragStartEvent);
          EventHelpers.addEvent(dragNode, 'drag', dragEvent);
          EventHelpers.addEvent(dragNode, 'dragend', dragEndEvent);
       }
    
       /*
        * The dragstart event handler logs to the user when the event started.
        */
       function dragStartEvent(e) {
          eventNoticeNode.innerHTML =
             sprintf("<strong>%s</strong>: Drag Event started.<br />%s",
                new Date(),  eventNoticeNode.innerHTML);
       }
    
       /*
        * The drag event reports to the user that dragging is on.
        */
       function dragEvent(e) {
          dragEventNoticeNode.innerHTML = "Currently dragging.";
       }
    
       /*
        * The dragend event logs to the user when the event had finished *and*
        * also reports that dragging has now stopped.
        */
       function dragEndEvent(e) {
          eventNoticeNode.innerHTML =
             sprintf("<strong>%s</strong>: Drag Event stopped.<br />%s",
                new Date(), eventNoticeNode.innerHTML);
          dragEventNoticeNode.innerHTML = "Dragging stopped."
       }
    }
    
    // fixes visual cues in IE and Chrome.
    DragDropHelpers.fixVisualCues=true;
    
    EventHelpers.addPageLoadEvent('dragObject.init');

    Before we get to the drag events, a word about the coding conventions I use for this and all the other code examples in this article:

    • the var me = this line ensures that the script doesn’t confuse the this keyword of the object with the this keyword inside an event handler.
    • EventHelpers.addPageLoadEvent() (which is part of EventHelpers.js) will execute dragObject.init when the HTML has loaded. addPageLoadEvent() is based on code from Dean Edwards’ article The window.onload Problem – Solved!. It is similar to jQuery’s $(document).ready()method and prototype’s dom:loaded event.
    • This code uses a JavaScript version of sprintf() that I had mentioned in a previous blog post. I have used it in all the remaining examples because I think it makes the code a lot easier to read (however, it is not necessary in order to make HTML5 drag and drop work).

    Let’s get back to the drag events — you’ll notice that I have set the dragstart, drag and dragend events. When this page is loaded, it will tell you if the draggable object is currently being dragged, and will log all dragstart and dragstop events on-screen. Click on the example link below and see for yourself:

    See Example #2, a draggable object with events attached

    Step 3: Setting Events on the Target Object

    Step 2 showed us how we can drag an object around the screen and know when each drag starts and ends. Now we need to drop the object. Let’s define a drop target to be an object where draggable objects can be dropped. A drop target can have the following event handlers attached to it:

    Event name Description
    dragenter Fires when a draggable object is first dragged inside an object.
    dragover Fires everytime a draggable object is moved inside an object. Note: if you want to allow the draggable object to be dropped inside this object, you must cancel the default behaviour of this event handler.
    dragleave Fires when a draggable object is dragged out of an object.
    drop Fired when a draggable object is dropped into an object.

    If you want an object to become a drop target, you must attach both the dragover and drop events to it. You may ask “I understand why I need to implement drop (after all, it is one half of the term “drag and drop”) but why do I need dragover?” As stated in the table above, the drop event will not fire unless you cancel the default behaviour of the event target’s dragover event. This point is really important and your scripts will not work correctly unless you do this.

    You may ask “Why is there even a dragover event in the first place? Isn’t drag sufficient?” (In his article, Peter-Paul Koch asked this question a bit more emphatically that I did). The dragover event object does contain some useful information, such as the coordinates of the mouse pointer inside the event target, (When I created DragDropHelpers, this information allowed me to fix Explorer and Chrome’s visual cue issue that I mentioned in step #1). These coordinates are stored in the properties offsetX and offsetY, except in Firefox 3.5 which keeps these values inside of the event properties layerX and layerY. For convenience, DragDropHelpers includes a method called getEventCoords() that will use the appropriate property to get these values (Note: layerX and layerY are only accurate when the drop target’s CSS position propety is set to relative, absolute or fixed).

    To show how to add drop functionality to your code, lets take the code from example #2 and add a drop target:

    <!DOCTYPE html>
    <html lang="en">
       <head>
          <title>Example #3: a dragable object with a drop target</title>
          <script type="text/javascript" src="../../shared/js/sprintf.js"></script>
          <script type="text/javascript" src="../../shared/js/EventHelpers.js"></script>
          <script type="text/javascript" src="../../shared/js/DragDropHelpers.js"></script>
    
          <script type="text/javascript" src="js/03-dragObjectWithTargetObject.js"></script>
    
          <link rel="stylesheet" type="text/css" media="screen" href="css/test1.css" />
    
       </head>
       <body>
    
          <h1>Example #3: a dragable object with a drop target</h1>
    
          <p>Try to drag the red box around and dropping it in the target object.</p>
    
          <p><strong>Is dragging:</strong> <span id="dragEventNotice">no</span>
    
          <p id="eventLog"><strong>Event now firing:</strong><br /><span id="eventNotice"></span>
          <a class="goBack" href="http://www.useragentman.com/blog/2010/01/10/cross-browser-html5-drag-and-drop/">Go back to the User Agent Man HTML5 Drag and Drop article</a></p>
    
          <a href="#" id="toDrag" draggable="true">This is a draggable item</a>
          <div id="dropTarget">This is a "target" object</div>
    
       </body>
    </html>
    

    The drop target will be the <div> with an id of dropTarget. The 03-dragObjectWithTargetObject.js script is the same code in example #2 except we add two event handlers:

    • a dragover event that reports where the mouse is inside dropTarget
    • a drop event that reports when the drop happened.
    var dragObject = new function () {
       var me = this;
    
       var dragNode, targetNode;
       var eventNoticeNode, dragEventNoticeNode;
       me.init = function () {
    
       	if (EventHelpers.hasPageLoadHappened(arguments)) {
       		return;
       	}	
    
       	dragNode=document.getElementById('toDrag');
       	targetNode=document.getElementById('dropTarget');
       	eventNoticeNode = document.getElementById('eventNotice');
       	dragEventNoticeNode = document.getElementById('dragEventNotice');
    
       	/* These are events for the draggable object */
       	EventHelpers.addEvent(dragNode, 'dragstart', dragStartEvent);
       	EventHelpers.addEvent(dragNode, 'drag', dragEvent);
       	EventHelpers.addEvent(dragNode, 'dragend', dragEndEvent);
    
       	/* These are events for the object to be dropped */
       	EventHelpers.addEvent(targetNode, 'dragover', dragOverEvent);
       	EventHelpers.addEvent(targetNode, 'drop', dropEvent);
    
       }
    
       function dragStartEvent(e) {
       	showMessage("Drag Event started");
       }
    
       function dragEvent(e) {
       	dragEventNoticeNode.innerHTML = "Currently dragging.<br />";
       }
    
       function dragEndEvent(e) {
       	showMessage("Drag Event stopped");
       	dragEventNoticeNode.innerHTML = "Dragging stopped."
       }
    
       function dragOverEvent(e) {
       	var coords = DragDropHelpers.getEventCoords(e);
       	showMessage(sprintf(
       	   "Drag over event happened on node with id %s at coordinate (%d, %d)",
       	   this.id, coords.x, coords.y));
       	EventHelpers.preventDefault(e);
       }
    
       function dropEvent(e) {
       	showMessage("Drop event happened on node with id " + this.id);
       	EventHelpers.preventDefault(e);
       }
    
       function showMessage(message) {
       	eventNoticeNode.innerHTML =
       		sprintf("<strong>%s</strong>: %s<br />%s",
       			new Date(), message, eventNoticeNode.innerHTML);
       }
    
    }
    
    // fixes visual cues in IE and Chrome.
    DragDropHelpers.fixVisualCues=true;
    
    EventHelpers.addPageLoadEvent('dragObject.init');

    (Note: EventHelpers.preventDefault(e) does what you’d expect: it prevents the default behavior of the event handler, similar to prototype’s Event.stop() or jQuery’s event.preventDefault())

    See Example #3: a dragable object with a drop target

    Note that in the above example, the time that the draggable object’s dragend event fires and the time that the drag target’s drop event fires is not consistant among browsers. Safari and Chrome fire dragend first, while Firefox and Internet Explorer fire drop first.

    The two other drop target events, dragenter and dragleave are not absolutely necessary for every script, but let’s add these events to example #3 anyway to see how they work:

    var dragObject = new function () {
       var me = this;
    
       var dragNode, targetNode;
       var eventNoticeNode, dragEventNoticeNode;
       me.init = function () {
    
          if (EventHelpers.hasPageLoadHappened(arguments)) {
             return;
          }   
    
          dragNode=document.getElementById('toDrag');
          targetNode=document.getElementById('dropTarget');
          eventNoticeNode = document.getElementById('eventNotice');
          dragEventNoticeNode = document.getElementById('dragEventNotice');
    
          /* These are events for the draggable object */
          EventHelpers.addEvent(dragNode, 'dragstart', dragStartEvent);
          EventHelpers.addEvent(dragNode, 'drag', dragEvent);
          EventHelpers.addEvent(dragNode, 'dragend', dragEndEvent);
    
          /* These are events for the object to be dropped */
          EventHelpers.addEvent(targetNode, 'dragover', dragOverEvent);
          EventHelpers.addEvent(targetNode, 'drop', dropEvent);
          EventHelpers.addEvent(targetNode, 'dragenter', dragEnterEvent);
          EventHelpers.addEvent(targetNode, 'dragleave', dragLeaveEvent);
       }
    
       function dragStartEvent(e) {
          showMessage("Drag Event started");
       }
    
       function dragEvent(e) {
          dragEventNoticeNode.innerHTML = "Currently dragging.<br />";
       }
    
       function dragEndEvent(e) {
          showMessage("Drag Event stopped");
          dragEventNoticeNode.innerHTML = "Dragging stopped.";
       }
    
       function dragOverEvent(e) {
          var coords = DragDropHelpers.getEventCoords(e);
    
          showMessage(sprintf(
             "Drag over event happened on node with id %s at coordinate (%d, %d)",
             this.id, coords.x, coords.y));
    
          EventHelpers.preventDefault(e);
       }
    
       function dropEvent(e) {
          showMessage("Drop event happened on node with id " + this.id);
          EventHelpers.preventDefault(e);
    
       }
    
       function dragEnterEvent(e) {
          showMessage("Drag Enter event happened on node with id " + this.id);
       }
    
       function dragLeaveEvent(e) {
          showMessage("Drag Leave event happened on node with id " + this.id);
       }
    
       function showMessage(message) {
          eventNoticeNode.innerHTML =
             sprintf("<strong>%s</strong>: %s<br />%s",
                new Date(), message, eventNoticeNode.innerHTML);
       }
    
    }
    
    // fixes visual cues in IE and Chrome.
    DragDropHelpers.fixVisualCues=true;
    
    EventHelpers.addPageLoadEvent('dragObject.init');

    See Example #3a: a drop target with dragenter and dragleave event handlers

    (Note that in Firefox 3.5, a dragleave event will fire just before a drop event, which the other browsers don’t fire dragleave when dropping).

    Step 4: Passing Data Between the Draggable and Target Objects

    Once a draggable object is dropped into a drop target, the two objects can pass information between them using the drop event’s dataTransfer property. This property has two methods, setData() and getData(). In order to pass data between the two, a developer must use setData() during one of the draggable node’s drag events (e.g. dragstart). The drop target can then receive this data during one of it’s events using the getData() method.

    Method Description
    setData(dataType, data)

    Sets the data that can be shared between the draggable node and the drag target.

    Parameters

    • dataType – The type of data that is to be set. So far, the only cross-browser dataTypes that can be set are ‘Text’ and ‘Url’.
    • data – The data that is going to be set.
    getData(dataType)

    Gets the data that was previously set by dataTransfer.setData(). It can also get data that was set by a dataTransfer.setData() call from another page, another browser window, and another vendor’s web browser on the same machine. It also can get data that was set by a drop event of another desktop application (for example, Windows WordPad).

    Parameters

    • dataType – The type of data that is to be set. So far, the only cross-browser dataTypes that can be set are ‘Text’ and ‘Url’.

    setData() takes one string parameter, which is the data you want to share between the draggable object and a drop target.

    Let’s take the code from step #3 and mix it up a bit: instead of having one draggable object, let’s make four. I have taken four photos of the Beatles from the Wikmedia Commons and made them draggable.

    <!DOCTYPE html>
    <html lang="en">
       <head>
          <title>Test 4: setData() and getData()</title>
          <script type="text/javascript" src="../../shared/js/sprintf.js"></script>
          <script type="text/javascript" src="../../shared/js/helpers.js"></script>
          <script type="text/javascript" src="../../shared/js/DragDropHelpers.js"></script>
    
          <script type="text/javascript" src="js/04-setDataGetData.js"></script>
    
          <link rel="stylesheet" type="text/css" media="screen" href="css/test1.css" />
    
       </head>
       <body>
    
          <h1>Test 4: <code>setData()</code> and <code>getData()</code></h1>
    
          <img draggable="true" src="images/george.png" alt="George Harrison" />
          <img draggable="true" src="images/john.png" alt="John Lennon" />
          <img draggable="true" src="images/paul.png" alt="Paul McCartney" />
          <img draggable="true" src="images/ringo.png" alt="Ringo Starr" />
    
          <div id="dropTarget"><span>Drop an image here to find out more information about it.</span></div>
    
       </body>
    </html>

    The script 04-setDataGetData.js uses dataTransfer.setData() and .getData() to copy the <img> of the Beatle being dragged, as well as placing the alt attribute’s contents underneath the image.

    var dragObject = new function () {
       var me = this;
    
       var targetNode;
       var eventNoticeNode, dragEventNoticeNode;
    
       var dataTransferCommentString;
    
       me.init = function () {
    
          if (EventHelpers.hasPageLoadHappened(arguments)) {
             return;
          }   
    
          targetNode=document.getElementById('dropTarget');
          eventNoticeNode = document.getElementById('eventNotice');
          dragEventNoticeNode = document.getElementById('dragEventNotice');
    
          /* These are events for the draggable objects */
          var dragNodes = cssQuery('[draggable=true]');
          for (var i = 0; i < dragNodes.length; i++) {
             var  dragNode=dragNodes[i]
             EventHelpers.addEvent(dragNode, 'dragstart', dragStartEvent);
          }
    
          /* These are events for the object to be dropped */
          EventHelpers.addEvent(targetNode, 'dragover', dragOverEvent);
          EventHelpers.addEvent(targetNode, 'drop', dropEvent);
       }
    
       function dragStartEvent(e) {
          e.dataTransfer.setData('Text',
             sprintf('<img src="%s" alt="%s" /><br /><p class="caption">%s</p>',
                this.src, this.alt, this.alt
             )
          );
       }
    
       function dragOverEvent(e) {
          EventHelpers.preventDefault(e);
       }
    
       function dropEvent(e) {
          this.innerHTML = e.dataTransfer.getData('Text');
          EventHelpers.preventDefault(e);
       }
    
    }
    
    // fixes visual cues in IE and Chrome.
    DragDropHelpers.fixVisualCues=true;
    
    EventHelpers.addPageLoadEvent('dragObject.init');

    See Example #4: using setData() and getData()

    As previously mentioned the data that is set in setData() can be read in other applications. If you are in Windows, open up WordPad and drag one of the images into it. You’ll see the data that was given to setData() during the dragstart event.

    This interoperability with the operating system will allow developers to do some very interesting things which are out of the scope of this article. I will, however, want to explore this in future postings.

    Step 5: Drag and Drop Effects

    In a traditional desktop application, you can use drag and drop to copy objects, move objects, and create links to things. You can use the HTML5 drag and drop events to do this as well, but it would be nice to give the user visual cues to show what kind of action a draggable object can do, and what kind of action a drop target can accept. It would also be nice to only allow “copy objects” to only drop on drop targets that are programmed to accept them.

    These features can be implemented using e.dataTransfer.effectAllowed on the draggable object and e.dataTransfer.dropEffect()on the drop target:

    Method Description
    e.dataTransfer.effectAllowed Sets the type of effect a draggable object is allowed to make. Valid values are copy, move, link, copyMove, copyLink, linkMove, all, and none.
    e.dataTransfer.dropEffect Sets the type of effect a drop target is allowed to accept. Valid effects include copy, move, and link.

    When these are set correctly, drag and drop will only work when the draggable object’s effectAllowed and the drop target’s dropEffect are compatible. To illustrate this, let’s first change the dragstart and dragover events in example #4 to do the copy effect:

       function dragStartEvent(e) {
          e.dataTransfer.effectAllowed="copy"; 
    
          e.dataTransfer.setData('Text',
             sprintf('<img src="%s" alt="%s" /><br /><p class="caption">%s</p>',
                this.src, this.alt, this.alt
             )
          );
       }
    
       function dragOverEvent(e) {
          e.dataTransfer.dropEffect = "copy";
          EventHelpers.preventDefault(e);
       }

    Example #5: matching drag and drop effects

    Note that unlike example #4, the mouse pointer has changed from the copy icon (in Windows, [Windows Copy Icon]) to the move icon ([Windows Move Icon]).

    Now let’s change the code so that the draggable object and the drop target don’t have matching effects:

       function dragStartEvent(e) {
          e.dataTransfer.effectAllowed="copy"; 
    
          e.dataTransfer.setData('Text',
             sprintf('<img src="%s" alt="%s" /><br /><p class="caption">%s</p>',
                this.src, this.alt, this.alt
             )
          );
       }
    
       function dragOverEvent(e) {
          e.dataTransfer.dropEffect = "move";
          EventHelpers.preventDefault(e);
       }

    See Example #5a: unmatching drag and drop effects

    You’ll see that the user is not able to drop the image on the drop target.

    Update (Jan 28, 2010): There is a documented bug in Safari and Chrome for Windows where drag and drop will not occur if effectAllowed and dropEffect are set to move. This bug does not occur in the Mac editions of these browsers.

    A More Complex Example

    Let’s take all the information we have gathered and make a “real-world” script. Let’s say you were asked to create a “user entitlement” administration widget for a website. The widget will have three entitlement catagories: “Unassigned Users”, “Restricted Users” (e.g. users who have restricted access to the website) and “Power Users” (e.g. users who would have administration access to a website):

    Screenshot of the user entitlement screen we want to implement

    Screenshot of the user entitlement screen we want to implement

    It should be rather easy for an administrator to drag and drop users back and forth between entitlement “buckets” in order to elevate or lower a users credentials.

    Let’s go through the steps we established above to implement this script.

    Step 1: Defining a Draggable Objects

    First let’s take a look at the table in the HTML:

    <table>
        <thead>
            <tr>
                <th>Unassigned Users</th>
                <th>Restricted Users</th>
                <th>Power Users</th>
            </tr>
        </thead>
    
        <tbody>
            <tr>
                <td id="unassignedUsers">
                	<a href="#" draggable="true">Moe Howard</a>
    		<a href="#" draggable="true">Curly Howard</a>
    		<a href="#" draggable="true">Shemp Howard</a>
                	<a href="#" draggable="true">Larry Fine</a>
    	    </td>
    
                <td id="restrictedUsers">
                </td>
    
                <td id="powerUsers">
                </td>
            </tr>
        </tbody>
        <tfoot>
    	<td id="unassignedUsersHelp">
    	  Drag a user from this list to another list to change the
    	  user's permissions.
    	</td>
    	<td id="restrictedUsersHelp">
    	  Dragging user here will give this user restricted
    	  permissions.
    	</td>
    	<td id="powerUsersHelp" >
    	  Dragging a user here will give this user power user access.
    	</td>
        </tfoot>
    </table>

    (Note that the contents in the tfoot node are hidden by CSS).

    Step 2: Setting Events on the Draggable Objects

    The “users” are draggable objects. In the script, we find all these nodes using Dean Edwards’ cssQuery and set dragstart and dragend event handlers on each of them (Note: cssQuery allows us to select nodes by CSS selector, similar to the built-in functionality inside jQuery).

    userNodes = cssQuery('[draggable=true]');
    for (var i=0; i<userNodes.length; i++) {
       EventHelpers.addEvent(userNodes[i], 'dragstart', userDragStartEvent);
       EventHelpers.addEvent(userNodes[i], 'dragend', userDragEndEvent);
    }

    It then keeps track of the current node being dragged in the object variable currentlyDraggedNode and makes that node transparent by making it a member of the draggedUser class, which the page’s stylesheet defines as transparent.

    function userDragStartEvent(e) {
       currentlyDraggedNode = this;
       currentlyDraggedNode.className = 'draggedUser';
    }

    The dragend event removes the transparency of the currentlyDraggedNode:

    function userDragEndEvent(e) {
       currentlyDraggedNode.className = '';
    }

    Step 3: Setting Events on the Target Objects

    All the table cells in the <tbody> are drop targets, so we use cssQuery again to grab all those nodes and set the appropriate events.

    userListNodes = cssQuery('.userList');
    
    for (var i=0; i<userListNodes.length; i++) {
       var userListNode = userListNodes[i];
       EventHelpers.addEvent(userListNode, 'dragover', cancel);
       EventHelpers.addEvent(userListNode, 'dragleave', userDragLeaveListEvent);
       EventHelpers.addEvent(userListNode, 'drop', userDropListEvent);
       EventHelpers.addEvent(userListNode, 'dragenter', userDragOverListEvent);
    }

    The most important event is the drop event handler. This event handler takes the currentlyDraggedNode, removes it from its current table cell (using removeChild()) and drops it into the drop target (using appendChild()).

    function setHelpVisibility(node, isVisible) {
       var helpNodeId = node.id + "Help";
       var helpNode = document.getElementById(helpNodeId);
    
       if (isVisible) {
          helpNode.className =  'showHelp';
       } else {
          helpNode.className =  '';
       }
    }
    
    function userDropListEvent(e) {
       currentlyDraggedNode.parentNode.removeChild(currentlyDraggedNode);
       this.appendChild(currentlyDraggedNode);
       setHelpVisibility(this, false);
       userDragEndEvent(e);
    }

    They show the contents of the table cell below it by making it a member of the class showHelp. They also

    prevent the dragover default behaviour so that drop events will fire in the drop target.

    function userDragOverListEvent(e) {
       setHelpVisibility(this, true);
       EventHelpers.preventDefault(e);
    }

    The dragleave re-hides the contents of the table cell below it.

    function userDragLeaveListEvent(e) {
      setHelpVisibility(this, false);
    }
    function userDragEndEvent(e) {
       currentlyDraggedNode.className = '';
    }

    Steps 4 and 5

    We do not actually pass any data between the draggable object and the drag target — when the drop event occurs, the draggable object is moved from one column to another without the need of any dataTransfer data. The drag and drop effects were already handled in step 3, so we are all done.

    See the above example in action

    Drag and Drop Between Frames

    Using the above information, it is also possible to drag and drop objects between frames (this is something that I don’t think any old school drag and drop script can do). I made an example below, and I leave it as an exercise to the reader to go through the code and figure out how it works.

    See the inter-frame drag and drop example in action

    Cross-Browser Issues I Have Seen

    These are the cross-browser issues I know of so far. I will update this list with others I find in the future.

    • in Firefox 3.5, a dragleave event will fire just before a drop event, which the other browsers don’t fire dragleave when dropping.
    • the time that the draggable object’s dragend event fires and the time that the drag target’s drop event fires is not consistant among browsers. Safari and Chrome fire dragend first, while Firefox and Internet Explorer fire drop first.
    • According to Francisco Tolmasky, setData() cannot be called during drag events in Firefox. Hopefully this will be fixed in a future release of the browser.
    • DragDropHelpers implements a workaround for Explorer so it can drag any object around. However, I did not use this functionality in any of the examples because it exposes a problem with Safari not being able to drag an object correctly when the user initiates the drag on the text inside it. This problem doesn’t happen with text inside of links, but with text inside other nodes (like a <div> tag). I understand this problem has been fixed in the Safari nightly builds, so I left the functionality in the DragDropHelpers library for future use.
    • DragDropHelpers.js “fixes” the dragging visual cues in Explorer and Chrome by:
      • making a transparent and absolutely positioned clone of a draggable object when it’s dragstart event is fired
      • moving the cloned object near the mouse when the <body>‘s drag event is fired.
      • destroying the clone when draggable object’s dragend event is fired.

      I didn’t turn it on by default because I am not sure when (or if) this problem will be fixed in Internet Explorer or Chrome. I also doesn’t work well when dragging objects from one window or frame to another, since the cloned visual cue can’t jump from page to page. Feel free to use it (or not) as you see fit.

    • As mentioned previously, because of a bug in Webkit, drag and drop will not occur if effectAllowed and dropEffect are set to move in Safari 4.0.4. Hopefully this will be fixed in a future version of Webkit.

    Conclusion

    HTML5 drag and drop will be an important part of our front-end toolkit. Even though the flavors that browser-vendors offer today are incomplete, it is possible to smooth out these flaws to produce useful web applications. What I have covered here only scratches the surface – there are more properties and methods that some of browsers offer that can be used to further enhance the drag and drop experience. But I have been working on this blog entry way to long, and my wife is giving me that “what the hell are you doing up at this hour” look, so I think I’ll stop here for now.

    Download

    DragDropHelpers, and all code used in this article can be downloaded below.

    Download the Latest Version of DragDropHelpers.js from GitHub.

    Tags: Drag and Drop · HTML5 · JavaScript · , , , , , , , , , , , , , , ,

    68 responses so far ↓
    • 1 Ryan // Jan 11, 2010 at 12:54 am

      Nice write up. One thing that Firefox has that is very useful is their :-moz-drag-over pseudo-selector to give visual cues when a user drags over a potential drag drop target.

    • 2 zoltan // Jan 11, 2010 at 1:05 am

      @ryan: Thanks for the info. It would be useful to have a common way for dropTargets so they can be easily styled.

    • 3 dave // Jan 11, 2010 at 6:29 pm

      Lots of really good information on the upcoming HTML 5 Drag and Drop feature. I’ll give it a try in my next project. I think it’s a powerful feature even though the browser support isn’t good yet.

      Keep up the good work and thanks for sharing.

    • 4 zoltan // Jan 11, 2010 at 6:42 pm

      @dave: I was surprised on how much I could do today. There is enough there to be useful – thought I can’t wait until the browsers support the HTML5 File API so we can do some really cool stuff with it.

    • 5 Anthony // Jan 28, 2010 at 8:08 pm

      Excellent post, zoltan! This is exactly what i am looking for. I hope this is enough for me to start my new DnD project for SharePoint. Really appreciate it!

    • 6 Daniel Huckstep // Feb 2, 2010 at 4:24 pm

      I think when you say “HTML5 Drag and Drop is supported in Firefox 3.5+, Chrome 3.0+, Safari 3.0+ and Internet Explorer 5.0.” you mean IE 8.0.

    • 7 russbuelt // Feb 2, 2010 at 4:26 pm

      Nice article!

      For some reason your examples do not work in Safari Version 4.0.4 (6531.21.10) or Webkit Nightly (6531.21.10, r53765) on OS X 10.6.2. Firefox 3.5.5 works fine. =)

    • 8 Brian // Feb 2, 2010 at 4:42 pm

      I’m running Firefox 3.6 on Windows and don’t get the cloning effect – it’s behaving like Chrome / IE

    • 9 Shawn K. // Feb 2, 2010 at 11:03 pm

      Very nice writeup, I hadn’t realized HTML5 added more event handlers.
      This will be quite nice not having to manually code drag and drop events.

    • 10 zoltan // Feb 2, 2010 at 11:57 pm

      @Daniel: You are not the only person who has said this to me. I can understand why – it through me for a double take as well, but HTML5 Drag and Drop is based on Microsoft’s implementation included in IE a while back (Nicholas C. Zakas’ article, Drag and Drop In Internet Explorer dated October 23, 2002, describes it in detail). From what I understand, it was decided to use Microsoft’s model as a basis since it works already and developers wouldn’t have to wait for Microsoft to play catch up like they have to do with, say, CSS3 border-radius and box-shadow.

    • 11 zoltan // Feb 3, 2010 at 12:05 am

      @russbuelt: I know that there a bug in Safari and Chrome for Windows where drag and drop will not occur if effectAllowed and dropEffect are set to move. I see that the first example (i.e. the permission form with the names of the Three Stooges) doesn’t work in those browsers but the others (with the red box) do. Are you seeing something different? Please let me know if you have time, and thanks for noticing this.

      I will fix the first example and repost tomorrow.

      Z.

    • 12 zoltan // Feb 3, 2010 at 12:12 am

      @Brian: Wow … that is interesting. I am not seeing that with my copy of Firefox 3.6. I am wondering if it has anything to do with the version of your OS or your version of DirectX, etc (I don’t know what it would be … I am just guessing here). Any information that you, or anyone else with a similar problem, could share would be awesome.

      z

    • 14 PHPGangsta // Feb 7, 2010 at 6:28 pm

      Whao, very long article with many many examples and Code, thank you very much for that information! I#M not a frontend guy, but I hope we get more intuitive websites in the future!

    • 15 Sebastiaan // Mar 10, 2010 at 3:43 am

      Unfortunately this doesn’t work for me in Opera 10.5 final on Win XP. Anyone else seeing this work on Opera?

    • 16 John B // Mar 24, 2010 at 7:40 am

      Hi Zoltan! I was feeling discouraged after reading PPK’s rant, but you’ve got an amazing resource here.

      I have discovered an issue. Several of your examples don’t work in Chrome on OSX, (I’m running 5.0.307.11 on OSX 10.6.2). Specifically, from Example #3 onwards, the drop event never fires. However, I found that this can be fixed by making sure that effectAllowed and dropEffect are properly set.

      Also, the visual fix for IE & Chrome doesn’t seem to be working for me, but I haven’t dived into why yet.

      Thanks again.

    • 17 zoltan // Mar 24, 2010 at 3:24 pm

      @John: Thanks for the info regarding Mac Chrome .. when writing the article, I was testing with version 4, which works as expected, but have confirmed 5 doesn’t. I will update this article after testing your suggestion. Thanks for the heads up :)

      As for ie not showing visual cues, i am currently not experiencing this. Could you let me know which version of IE you tested this on?

    • 18 John B // Mar 24, 2010 at 4:43 pm

      RE: IE – I *think* I was testing on IE7/XP Home, but I don’t remember 100%, I’ve had both that and my IE8/Vista Home Premium up & running over the last couple of days.

      In Mac Chrome, (I haven’t tested Windows yet), I’ve discovered further issues. It seems that the dragover and drop events are not firing unless the draggable object is an a tag with the href set, (IE-ish, no?).

    • 19 Ken Corey // Apr 21, 2010 at 7:54 am

      Hiya,

      Just ran across this in a search to solve a problem I’m having. I tried it out on Firefox, and all worked fine.

      I’m trying it out on Chrome 5.0.342.9 on WinXP SP3, and the visual feedback didn’t appear, so apparently on line 148 of DragDropHelpers.js, you might want to change the version comparison to be less than 6 rather than 5…

      Even worse, the drop events aren’t firing at all, which is a crying shame. I haven’t dug into it enough to see what’s going on, but it looks like I’ll have to. I *need* this to work.

      -Ken

    • 20 Ken Corey // Apr 22, 2010 at 4:51 am

      Just a follow on for anyone coming after me…turns out that Chrome 5 has a default value of ‘uninitialized’ and ‘none’ for effectAllowed and dropEffect. Change these (both) to ‘copy’, and the drop events are sent using Chrome.

      (Look in example #5 matched to see where to do that.)

      -Ken

    • 21 zoltan // Apr 22, 2010 at 6:16 pm

      @Ken: thanks a mil for these — I will be updating DragAndDropHelpers.js with these changes very soon.

    • 23 Shreyas Patel // Aug 6, 2010 at 12:33 pm

      It’s Great Material Really

    • 24 Art4Med // Aug 27, 2010 at 8:56 pm

      Transparency on FireFox works for Ubuntu Lucid (10.04)

    • 25 Salman K // Sep 8, 2010 at 5:00 am

      Thank you so much for the info. Will prove immensely helpful to us in our efforts to get to grip with HTML5 DnD. Best,

    • 28 Santosh Kumar // Feb 7, 2011 at 4:50 am

      Very good article, Really we all(developers) are very thankfull to you.

    • 29 TH // Feb 27, 2011 at 11:15 am

      I´m trying to load this script through ajax, instead after page load, but I´m stuck! Can someone please help me with this? Is there a function to call or how can this be solved?

    • 30 Commandrea // Feb 28, 2011 at 1:13 am

      Super awesome script and educational to boot! IS there a way to drag something to a point to remove it entirely? Out of the columns or out of the frames or even into a ‘trash can’ of sort? I’m have a ton of fun playing around with this! Thanks!

    • 31 zoltan // Feb 28, 2011 at 11:35 am

      @TH: If you want to initialize DragDropHelpers.js after a page load, you must execute the DragDropHelpers.init() method. Try that and let me know if it does/doesn’t work.

    • 32 zoltan // Feb 28, 2011 at 11:45 am

      @Commandrea: Glad you like this — I will be making a follow up post soon with more things I have discovered about HTML5 Drag and Drop. To answer your question, yes, it is quite possible. You would have to do a removeChild() on the parent node of the object you are dragging when the drop event is fired. I do this on the “permissions form” example, except I also place it in the drop target by using its appendChild(). If you just want it to disappear, don’t use appendChild() and you are done. Take a look in the permissionsForm.js script’s userDropListEvent() method and you’ll see what I mean.

    • 33 TH // Feb 28, 2011 at 3:58 pm

      Thanks for your quick response! I´ve tried to execute DragDropHelpers.init() after xmlHttp.readyState==4, but with no luck. Any other suggestion on how to solve this?

    • 34 Viktor // Mar 3, 2011 at 6:45 pm

      The best article that I found so far about drag and drop! It saved me tons of time. Thank you very much, Zoltan!

      I have a couple of additional questions and suspect the answers, but would be very grateful if someone can help me to clarify them.

      1) How to drag/drop multiple elements?

      My application has a way to mark multiple elements as selected. One possible approach is to set “draggable” to “true” on an element when it is selected and set to “false” when it is unselected. When drag operation is started on one of the selected elements the “ondragstart” callback may find all other selected elements and attach them to a transfer object. After that everything should be similar to what was described with some minor changes. Is it the right way to support dragging of multiple selections?

      2) How to start drag/drop programmatically?

      All samples assume that drag is started automatically when mouse is clicked down. What to do if drag/drop was initiated in applet or Flex component on a page and target is in HTML part of the page? How to inform the HTML part of the page that the drag operation is in progress? With having API to start drag/drop I can at least see how I can work around.

      -Viktor

    • 35 zoltan // Mar 5, 2011 at 3:43 pm

      @Viktor: I have never thought about dragging multiple selections, so my immediate answer is “I don’t know”. However, I will be writing a follow up for this article soon (there are a few more interesting bits that I have discovered — I just need to write them down when I have a few moments), so I’ll experiment with this usecase and post my findings in a future article. Thanks for the great idea!

      As for the drag/drop programatially: it would definitely be worth exploring to see if one could do that by calling dispatchEvent()/fireEvent() using the drag events. Another great idea to look into.

      If you (or anyone else) finds the answers to these issues before I do, please post something here — I would definitely be interested in knowing what others find out here.

    • 36 Jeroen // Mar 29, 2011 at 6:36 pm

      Excellent post! Thanks so much.

      It appears that firefox has some interesting bugs (on mac, running 3.6).
      For instance, a div draggable=true doesn’t give you a drag image unless a draggable item precedes it in the layout. That could be an image, a link or some form element such as an input.
      Another funny thing: if you don’t allow a drop (so don’t prevent the default in the drag event), FF doesn’t fire dragleave on the drop target until you start dragging the drag object again. So if you style the drop target ondragenter, and undo that styling ondragleave, the styling is only removed when you drag again.
      I should post them to mozilla, but I haven’t completely figured out yet when these bugs do or don’t appear.

    • 37 zoltan // Mar 29, 2011 at 7:56 pm

      @Jeroen: interesting! Would you happen to have some examples of these situations that you can post on a web server someplace? I’d love to see!

    • 38 Daniel M // Mar 30, 2011 at 4:15 pm

      Does IE9 support HTML5 Drag and Drop?
      What about Firefox 4.0?

      You still working on a follow up article?

    • 39 zoltan // Mar 30, 2011 at 11:44 pm

      @Daniel: IE9 does support Drag and Drop. So does Firefox 4.0. And yes, I’ll be writing a follow up article. I have three on the back-burner right now, but since you are asking I’ll speed that one up a bit :-)

    • 40 Jeroen // Apr 7, 2011 at 7:01 am

      Zoltan,
      I put up an example on http://www.marritsnel.nl/dragdrop.html.
      First test: drag the blue square. On FF 3.6 (mac) I don’t get a drag image. In the source I put some examples of elements that enable the drag image, if they are present. So download the example and check for yourself. Maybe you get it working without those images (that is, if you reproduce it).
      Second test: drag the blue square over the red drop zone. This dropzone gets a blue border due to a class that’s set ondragenter. Then drop. The blue border remains. Then drag again. The blue border goes. This happens ondragleave. So that event is not fired correctly.

      Again, this is FF 3.6 mac. Haven’t tested windows or the nightlies yet. Safari/Chrome on the mac don’t exhibit this behaviour.

    • 41 Jeroen // Apr 7, 2011 at 7:09 am

      Well, don’t worry about that bug: I just downloaded FF 4 and both bugs are solved there.

    • 42 TheAce // Apr 11, 2011 at 4:20 pm

      In your excellent DragDropHelper.js, function doesShowVisualCues, the test for browser version regexp should probably be modified to /Chrome\/[0-9]*\.[0-9]*/ because the Chrome version is displayed as 10.0; the regexp assumes single digit version. Small stuff.

    • 43 Timo // Apr 12, 2011 at 5:34 am

      I have tested many Drag and Drop libraries and none of them has support for transformations. DragDropHelpers has the same problem. I made an example of this behavior: http://www.royalcomics.org/puhekupla/DragDropHelpers-1.0a/tests/dragAndDrop/05-dataTransfer.html

      Is it possible to preserve transformations?

    • 44 zoltan // Apr 15, 2011 at 9:54 am

      Thanks for your feedback. I am actually in the middle of updating DragDropHelpers.js and will include your fix (or an equivalent improvement) very soon.

    • 45 Ralph // May 25, 2011 at 4:24 am

      Great stuff! It was very usefull for me.

      Cheerz

    • 46 Yaffle // Jul 11, 2011 at 11:39 am

      I create a polyfill for Opera to support basic HTML5 drag and drop,

      https://github.com/Yaffle/polyfills/tree/master/dragAndDrop

      may be it will be interesting to somebody.

    • 47 zoltan // Jul 12, 2011 at 10:25 am

      Thanks for posting this! I will be looking at this with great interest to see how it can integrate with my examples.

    • 48 sofpy // Jul 15, 2011 at 10:33 am

      Great article!!life saver!!I do have a question, i can’t get the draggable element to drop just a copy of it and keep the original on its place. How do you do that in the 4th example??

    • 49 zoltan // Jul 15, 2011 at 4:14 pm

      @sofpy: If you look at the code above the “Example #4″ link, you will see that in the dragstart event, I call e.dataTransfer.setData('Text', html) where html is the HTML of the image I am dragging. This data is saved into memory so that when the object is dropped, it can be accessed by calling e.dataTransfer.getData('Text') in the drop event.

      I hope that makes sense. Please feel free to email me at the email address in the top sidenav if you would like to ask more detailed questions.

    • 50 Terese Wahlstrom // Aug 27, 2011 at 12:09 pm

      Hi,
      Great article. I happened to notice that in test 1a the red box didn’t clone, in the 1b however it did in internet explorer. It’s that due to the cue-fix or are there any thing I’ve missed?

    • 51 zoltan // Sep 2, 2011 at 8:06 am

      @Terese: could you let me know which browser you were using when it didn’t clone? It may be a bug my script has with one of the newer versions of the current web browsers. It has been more than a year since I posted this article, and I would very much like to keep it up to date.

      Thanks in advance!

    • 53 cypherljk // Sep 22, 2011 at 3:52 pm

      Hello,

      Excellent article. I was looking at this article and one other for drag and drop information. I have implemented this in an ASP / C# page and came across an issue. I have an update panel and I am dragging the contents from on panel to another. When the initial container panel is updated the images ‘loose’ their draggability. I solve this by calling ‘DragDropHelpers.init()’ and it allows me to drag an image however as soon as I drag the image it goes to the ‘no move’ icon as if there is not a drop target when there is. If I do not update the panel it is fine. Any thoughts?

    • 54 cypherljk // Sep 22, 2011 at 4:07 pm

      p.s. I actually had to comment out the line in DragDropHelper.init() where it checks if the pageload has happened and returns to get it to work to the point where it is now.

      Thx

    • 55 joubin // Oct 5, 2011 at 5:43 pm

      Great write up. Exactly what I needed. Can someone tell me how to pass the location of the user to mySQL db. via PHP preferably.

    • 56 zoltan // Oct 12, 2011 at 7:33 pm

      @joubin: I would have a hidden form on the page that would be populated with the right data on a drop event. That would be the most straightforward way to do this.

    • 57 Flo // Nov 22, 2011 at 5:59 am

      Hi, and thank you for your very good tuto !
      I had to make drag&drop with prototype, but I think it will be easier and faster to works with html5.
      For my work, I make a JS class with resizables Divs, and I need to include Drag&Drop option for Divs.
      You say that we couldn’t use htlm5 for Divs, is-there any option or update about that, or it’s really really only <a> and <img> ?

      And again, thanks for tuto !
      Florent.

    • 58 alanselvam // Jan 2, 2012 at 4:22 am

      superb article.. really helps to me. thx

    • 59 mgs227 // May 2, 2012 at 8:49 pm

      This is great… Is there a way for the source to also be a target and the target also be a source? I want to drag items from one list to the other. and back again – if needed, and also move items within a list – to change order…

    • 60 zoltan // May 8, 2012 at 10:40 pm

      @msg227: I am almost positive you can do that. If you try it and it doesn’t work, let me know. I’d be very interested to see why not.

    • 61 onur // Nov 3, 2012 at 9:06 am

      How can i use 3 drop box and how can it show these elements alt information?

    • 62 zoltan // Nov 4, 2012 at 12:16 pm

      @onur: Please send me an email at the address in the left nav on the top of the screen with details about what you mean. I’d love to help out but I need more information.

    • 63 John // Dec 11, 2012 at 8:45 pm

      Thank you so much for this tutorial. I really learned a lot. I’m sure I’ll be asking questions as I work this into my brain, but for now, do you have a DONATE button anywhere. I’d love to contribute to your Christmas.

    • 64 zoltan // Dec 12, 2012 at 3:56 pm

      @John: I keep going back and forth on having a donate button. I don’t want people to feel obligated to donate, but if people really want to, it would be nice to help pay the ISP bills. I’ll get back to you … thanks for the kind words. :-)

    • 65 joe // Jan 2, 2013 at 1:15 pm

      hi zoltan, i spent a lot of time through internet browsing for a good article explaining in an easy way drag and drop with html5. there are a lot of tutorials out there with either calling jQuery or Yahoo framework or the provided examples or code is oversized.
      so after 40 articles i can tell you for sure, that it was awesome explained and very helpful for me as well – so thank you for spending time write this article :-)

    • 66 Naeem // Mar 14, 2013 at 6:40 am

      Great article.

      It was my second day on HTML5 DnD, and it helped me a lot to complete the implementation :)

      Thanks man

    • 67 pdpdizon // May 28, 2013 at 7:27 am

      Hi Zoltan, I made a derivative work based on your source code. I discussed it here: http://webdesignforcsmajors.wordpress.com/2013/05/28/html5-fun-with-drag-and-drop/ . If you determine that you haven’t been credited properly, let me know so I can give you proper mention. Thanks!

    • 68 zoltan // Jun 16, 2013 at 12:41 pm

      @pdpdizon: Looks great! Thanks for the shout out!

    Give Feedback

    Don't be shy! Give feedback and join the discussion.

    Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.

    An orange star denotes a required field.