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.