Getting Started with HTML5 Drag and Drop Part IV: Multiple Draggables and Multiple Droppables

HTML5

HTML5 LogoOnce you know how to successfully drag one HTML element and drop it on another one, there is a new UX challenge: multiple elements that can be dragged and then dropped on any one of multiple droppables.

In Parts I, II and III of this series, we covered the critical concepts needed to implement HTML drag and drop. There, we talked about the dragstart event, the dataTransfer object, originalEvent object, drop event, dragenter event, dragleave event, and setDragImage method. These concepts got us to the point where we could drag one HTML element, and then drop it on another. All of this, with some UX considerations such as setting a custom drag image, updating the droppable element when something is dragged over it or when something is dropped on it.

When I was taking a deep dive into HTML5 drag-and-drop, I wanted to be able to wire-up a scenario where there were multiple “draggable” elements, and multiple “droppable” elements. So from a UX standpoint, my goals were:

  1. Any draggable could be dropped on any droppable
  2. Each droppable would change appearance when an element was dragged over it
  3. Each droppable would change appearance when an element was dropped onto it
  4. Each droppable would accept only one dropped element at a time
  5. The container that originally held the droppables would change appearance when empty
  6. The container that originally holds the droppables could accept any one or all of the droppables if they are dragged back to it

When I first attempted this, I got to a point where this all seemed like a lot of moving parts. Fortunately, a substantial portion of the challenge has been solved in parts I, II and III of this series, and I hope that if you have already read those articles, you have a solid understanding of how to do the following:

  1. Make an element draggable
  2. Set a custom drag element
  3. Make an element droppable
  4. Update the droppable when an element is dragged over it
  5. Update the droppable when an element was dropped onto it

So with that knowledge, our task list shrinks a bit:

  1. Any draggable can be dropped on any droppable
  2. Each droppable will accept only one dropped element at a time
  3. The container that originally held the droppables will change appearance when empty
  4. The container that originally held the droppables will accept any one or all of the droppables if they are dragged back to it

From here on, I will walk through the steps I took to accomplish these goals. There are probably other ways to go about solving these problems, but I am sharing the approach I took.

Task # 1: Any draggable can be dropped on any droppable

Well this was the easiest step. In parts I, II and III of this series, all of the JavaScript event binding was accomplished using classes instead of IDs. Because of this, adding more draggables and droppables in the HTML solved this problem. The existing JavaScript worked fine.

Task # 2: Each droppable will accept only one dropped element at a time

There are two things I needed to do in order to solve this problem: 1) create two separate “drop” event handlers, one for the multiple droppable HTML elements and another for the container that originally held the droppables; 2) in “drop” event handlers of the multiple droppables, check to see if the droppable element already has a child element.

Before I discuss the fix for task # 2, take a look at this JSFiddle link: http://jsfiddle.net/v9hawcof/5/

That link demonstrates the behavior that I do NOT want: more than one draggable can be dropped onto a droppable.

Base HTML

So in the code example above, we have the base HTML for the rest of the examples in this article. For brevity’s sake, I’ve stripped out all but the most essential areas of the markup so that you can focus on the technologies demonstrated in this article. When you view the page source for the final working example, you will see that there is more HTML, but it is for presentational purposes only.

Example # 1

Here is the JSfiddle link for Example # 1: http://jsfiddle.net/v9hawcof/6/

Now in Example # 1, I’ve added a new function called: “dropHandlerSingle”. This event handler is dedicated to the four light brown boxes at top (i.e. the “multiple droppables”). In the function I check to see if the droppable element has any children. If so, then I exit immediately. You may also notice that I trigger this custom event: “custom:dropEvent”. That will be explained shortly.

Task # 3: The container that originally held the droppables will change appearance when empty

Example # 2

Here is the JSfiddle link for Example # 2: http://jsfiddle.net/v9hawcof/7/

In Example # 2, you see the event handler for “custom:dropEvent”. The reason I used the setTimeout method, is because of timing challenges. When the drop event occurs, the DOM is not in the state needed in order to query it and figure out what is going on (i.e. which droppable elements have children). This event handler for “custom:dropEvent” will check each droppable element, and if it has no children, it removes the CSS class that gives it a “hasChildren” appearance (i.e. the background color changes from dark brown to light brown).

Task # 4: The container that originally held the droppables will change appearance when empty

Example # 3A

Example # 3B

Example # 3C

 Here is the JSfiddle link for Example # 3: http://jsfiddle.net/v9hawcof/8/

In Example # 3A, I bind an event handler for the container that originally held the droppables. In Example # 3B you’ll see that event handler: the “dropHandlerMultiple” function. The “dropHandlerMultiple” event handler does pretty much the same thing as the “dropHandlerSingle” event handler. The only difference is that the “dropHandlerMultiple” function does not check to see if it has children already. This is the critical step in accomplishing task # 4. In Example # 3B, you’ll see the code that tells the dragenter event handler to exit immediately if an element is dragged over the container that originally held the droppables. This is not a critical feature. I suppose I could have allowed the dragenter UX to apply to that container as well.

Here is the working example for this article: http://examples.kevinchisholm.com/html5/drag-and-drop/part-iv.html

Here is the JavaScript for this article’s working example: http://examples.kevinchisholm.com/html5/drag-and-drop/js/part-iv.js

Summary

As you can see, everything in this article is really a matter of personal choice. What I’ve done is detail how I decided to solve a particular UX challenge. Now how you approach this same set of tasks is completely up to you. I found that while attempting all of this, I learned a few things about HTML5 drag and drop. I hope you will too.

Getting Started with HTML5 Drag and Drop Part III: Setting a Custom Drag Image

HTML5

HTML5 LogoLearn how to set a custom drag image for a better drag and drop UX.

In previous articles of this series: “Getting Started with HTML5 Drag and Drop Part I: Introduction,” and “Getting Started with HTML5 Drag and Drop Part II: The dragenter and dragleave Events,” we covered the basics needed to implement HTML5 drag and drop. The second article focused on the UX for dropping a dragged element. In this article, we will cover a UX consideration for dragging: setting a custom drag image.

When dragging a draggable HTML element, the default drag image is the element being dragged. In most cases, this is probably just fine. But what about if you want to snazz-up the UX and specify a custom image for the drag operation?

Using an image that already exists on the page

When using an image that already exists in the page as your custom drag image, there are two restrictions:

  1. The image must exist in the DOM (i.e. it cannot have its “display” property set to “none,” or “visibility: hidden.”
  2. The image must be visible on the page (i.e. it must be visible without scrolling)

Example # 1

(for the base HTML, please refer to either Part I or Part II of this series; they both provide that markup).

Here is the JSfiddle link for Example # 1: http://jsfiddle.net/v9hawcof/3/

The setDragImage Method

In Example # 1, we have set the HTML5 logo as the custom drag image. We do this by using the document.getElementsByClassName method. Since this method returns an HTMLcollection, we reference the element by using the [0] property as we know we will only get one element back (yes, we could have used the document.getElementbyId method, but I wanted to keep the HTML clean).

There are a couple of drawbacks to this approach

  1. The HTML5 logo must be visible in the DOM, which is a bit restrictive
    Some browsers (e.g. Firefox) will display the image in its native size, whereas others (e.g. Google Chrome) might display it using the dimensions defined by its current CSS
  2. Using an external image for the custom drag image

Example # 2

Here is the JSfiddle link for Example # 2: http://jsfiddle.net/v9hawcof/4/

In Example # 2, we use an external image for our custom drag image. In this case it’s a silly image of Bart Simpson on his surfboard, but you can certainly use any image you desire. The advantage here is that we have much more control over the experience. We can use any image we like, and we don’t need to worry about whether or not the image we use is part of or visible in the DOM; it’s external to the DOM. Of course, you can use an image that already exists in the DOM, but you gain the greatest control when you create a new image, rather than use a reference to a DOM element.

Here is the working example for this article: http://examples.kevinchisholm.com/html5/drag-and-drop/part-iii.html

Here is the JavaScript for this article’s working example: http://examples.kevinchisholm.com/html5/drag-and-drop/js/part-iii.js

Summary

Setting a custom drag image may seem like icing on the cake, but it is not an uncommon case. In this article we discussed two options: using a reference to an existing image element in the DOM, as well as using an external image. There is no right or wrong answer as to which approach is best, but I hope this article has not only explained how to do this, but the pros and cons of each method.

Helpful Links for the HTML5 Drag and Drop setDragImage Method

https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer#setDragImage.28.29

http://help.dottoro.com/ljdpgfkx.php

http://stackoverflow.com/questions/16894049/feature-detection-for-setdragimage-of-html5-drag-and-drop

Getting Started with HTML5 Drag and Drop Part II: The dragenter and dragleave Events

HTML5

HTML5 LogoGetting HTML5 drag and drop to work is a great start, but taking this to production requires certain UX considerations.

In the previous article of this series: “Getting Started with HTML5 Drag and Drop Part I: Introduction,” we covered the bare minimum code needed to make an HTML element draggable, and droppable onto another element. So, in Part II of this series, I will talk about two events that allow for better UX with regard to any element onto which you might drop something.

When I say: “…..any element onto which you might drop something,” I am referring to the fact that dragging something does not always mean that you will definitely drop it. In most drag and drop scenarios, you have one or more elements to drag, but also multiple places where you may drop them. Therefore, it is most often a good idea to give the user some indication that the element that they are dragging could be dropped in a particular location. Consequently, this involves setting up a hover-like behavior where the droppable element reacts to the fact that a draggable element has been dragged over it, or dragged away from it.

HTML Base

In the code example above, we have the base HTML for the rest of the examples in this article. For brevity’s sake, I’ve stripped out all but the most essential areas of the markup so that you can focus on the technologies demonstrated in this article. When you view the page source for the final working example, you will see that there is more HTML, but it is for presentational purposes only.

Example # 1

Here is the JSfiddle link for Example # 1: http://jsfiddle.net/v9hawcof/

The dragenter Event

In Example # 1A, we bind an anonymous function to the dragenter events of the droppable HTML element. In this event handler, we add the class: “dragEnter,” which changes the background color of that element. The reason for doing this is to tell the user: “If you want, you can drop the element that you are dragging.” But if you click the link above for the JSFiddle example, you’ll see that we have a problem. Although we add the dragEnter class when the user drags an element over the droppable element, we have not set a handler for when the user drags that element away; we can’t always assume that the user will drop the dragged element. So, we have to handle the case in which the user drags the element over the droppable element, but does not drop it, and then drags away.

Example # 2

The dragleave Event

Here is the JS fiddle link for Example # 2: http://jsfiddle.net/v9hawcof/1/

In Example # 2, we bind an anonymous function to the dragleave event of the droppable element. In that event handler, we simply remove the “dragEnter” class from the droppable element. If you click the jsFiddle link for Example # 2, you’ll see that the user experience is much better; when you drag the small blue box over the larger tan box, the droppable element turns green. So, if you choose not to drop the blue box, and drag it away, the droppable element turns back to tan.

Here is the working example for this article: http://examples.kevinchisholm.com/html5/drag-and-drop/part-ii.html

Here is the JavaScript for this article’s working example:
http://examples.kevinchisholm.com/html5/drag-and-drop/js/part-ii.js

Summary

It is hard to imagine a case in which you would not want to notify the user in some way of the various events that take place during a drag and drop operation. In this article, we focused on the case in which the user drags an element over a droppable one, but does not drop it, and then drags the draggable element away. By attaching event handlers to the dragenter and dragleave events, we have a window of opportunity in which the UI can be updated accordingly. How you prefer to handle the UX in this and similar cases is up to you, but it is an important area to consider.

Helpful Links for HTML5 Drag and Drop dragenter and dragleave Events

dragenter

https://developer.mozilla.org/en-US/docs/Web/Events/dragenter

dragleave

https://developer.mozilla.org/en-US/docs/Web/Events/dragleave