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

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. In the second article, we 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 it’s “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 will display the image in its native size (e.g. Firefox), whereas others might display it using the dimensions defined by its current CSS (e.g. Google Chrome)
  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 is 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 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. 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. For this reason, 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. 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 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. 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

Getting Started with HTML5 Drag and Drop Part I: Introduction

HTML5 LogoWhile there are plenty of JavaScript libraries that take the pain out of drag-and-drop, understanding how to implement this feature of HTML5 is not only possible, it’s easier than you may think.

Let’s face facts: drag and drop is something that most of us take for granted. Ever since the late ‘80s, we’ve been able to drag something on the screen and then drop it somewhere as a way of saying: “I want this….” or “ ….do something to this.” In recent years, that kind of functionality has become more and more commonplace in desktop browsers. It’s hard to believe, but drag-and-drop was originally supported in Microsoft Internet Explorer 5. Yep, the XMLHttpRequest object is not the only game-changing feature originally dreamed-up at Microsoft. Ironically, our good friend IE is the only major browser whose support is limited. IE 10 seems to be fairly in-line with regard to the most critical features, but IE 9 and below lack features that make native drag and drop implementation frustrating.

Let’s close our eyes for a moment, and pretend we don’t need to support Microsoft Internet Explorer

Fortunately, the HTML5 specification includes a fairly robust API for drag and drop. Making HTML elements draggable and droppable is painless, and managing the related events is possible. The key to all of this is understanding the bare-minimum needed in order to properly manage this functionality.

But isn’t everything “draggable” these days?

No, not quite. According to the HTML5 Drag and Drop specification, only images and anchor links are draggable by default (and the anchor must have an href attribute). If you want to drag any other kind of HTML element, there is a bit of work to do.

Wait, aren’t there a gazillion JavaScript libraries that make drag and drop a snap?

Yep, there sure are. If you merely want to download a library or jQuery plugin and simply “dragify” as needed, this article probably isn’t for you. But if you want to understand how native HTML5 drag and drop functionality works, read on!

HTML Base

In the code example above, we have the base HTML for the rest of the examples in this article. For brevity 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.

Making an Element Draggable

In order to make an element draggable, you need only add a “draggable” attribute to that element, and set its value to “true”. This can be done by including the “draggable” attribute in the original html, or via JavaScript.

Example # 1A

In Example # 1A, we have made an HTML draggable by adding a “draggable” attribute to that element, and setting its value to “true”. While I could have simply added “draggable” attribute to the HTML, I chose to do so using JavaScript in order to illustrate how much control you have over this.

The dragstart Event

While the code in Example # 1A is completely valid, unless the element in question is an image, or an anchor tag (with an href attribute), there is a bit more work to do. Providing a “draggable” attribute alone is not enough to make an element draggable. There is at minimum, a second step: provide an event handler for the dragstart Event.

The dataTransfer Object

The dataTransferobject is used to hold the data that is being dragged during a drag and drop operation.

Even in the most basic drag and drop implementation, the dataTransfer object is important. The reason for this is the core logic behind drag and drop: why would you want to drag something if you did not plan to eventually drop it? (At first I found this frustrating; I felt that the “draggable” attribute alone should suffice when you want to be able to drag an element. But the more I delved-into the specification, the more the logic started to make sense.)

In order to drag an element, you’ll need to provide some data to the dataTransfer object. Ultimately, it does not matter what data you provide, you just need to use the setData method of the dataTransfer object to set some data. You must do this in the dragstart event. After your dragstart event handler returns, you cannot modify the dataTransfer object.

I bet I know what you are thinking now:

Aaaaaaaaaaaaahhhhhh, c’mon Kevin, another friggin’ object I gotta learn about? In the Intro you said something about this whole thing being “…easier than you may think” !!

I know. I kinda lied. But aren’t you glad you’re halfway across the river?

(Seriously, this is all no big deal. Considering how cool drag and drop is, learning about a few objects and methods is a small price to pay.)

The originalEvent Object

When thinking about drag and drop events, it becomes necessary to consider multiple event objects. For example, when you start dragging an element, it fires a “dragStart” event. But then consider the element that you might drop this element on. It fires a number of events as well. When handling any of these events, you will need access to the original event that fired, as a way to refer to the HTML element that was dragged. So, in your dragstart event handler, you’ll want to create a reference to the dragged HTML element itself. This way, in any of the events fired by the droppable element, you can obtain a reference to the element that was dragged (i.e. the element that you might want to drop onto the droppable element).

Example # 1B

In Example # 1B, we bind an event handler for the dragstart event of the HTML element that we plan to drag. That handler is the function: dragStartHandler. The dragStartHandler function takes the current event as its first argument. We then use the setData method of the originalEvent’s dataTransfer object. The originalEvent object is a property of the current event. In this case, the originalEvent is the dragStart event. When calling the setData method, we pass “Text” as the first argument. That simply tells the setData method what type of data we are about to set. The second argument is the ID attribute of the element that is being dragged. This is accomplished by referencing the target object of the event, which is the element that is being dragged, and then use the target object’s getAttribute method to retrieve the ID attribute of the target element.

Setting Up the Droppable Element

You don’t need to do anything special to allow one HTML element to be dragged over another. Once an element is properly “draggable,” you can drag it anywhere you like. But just like playing cards with Kenny Rogers, that gets old pretty quick. The next logical step in implementing HTML5 drag and drop is setting up the droppable element(s) so that you can handle the various events that fire during the process.

The dragover and drop Events

Example # 2

The dragover Event

In Example # 2, we have bound two event handlers to the droppable HTML element. The allowDragover function is bound to that element’s dragover event, and as you can see, it has little functionality: it simply prevents any default behavior that the browser might implement. Surprisingly, this is critical; the drop functionality will not work correctly without it. This is one event handler that you have to simply “set and forget.”

The drop Event

In Example # 2, we bind the dropHandler function to the drop event of the droppable element. This is where the real action should take place.

Example # 3

In Example # 3, we’ve added some functionality to the dropHandler function. First, we prevent any default behavior that the browser might have implemented. Next, we retrieve the ID of the HTML element that was dragged, leveraging the work that we did in the dragStartHandler function. The steps here are virtually the opposite of those taken to set the ID of the dragged element: we use the getData method of the originalEvent’s dataTransfer object, which itself is a property of the event object’s originalEvent property.

Next, we add the class “hasChild” to the element that received the drop. This is for presentational purposes only, but it does allow us to provide a better user experience by changing the appearance of the element that received the drop. Then we “move” the element simply by appending it to the droppable element, and then change the dragged element’s innerHTML to “Dropped”. Just like changing the appearance of the droppable element, this kind of approach usually makes for a better user experience.

Example # 4

In Example # 4, we have the full code for our working example.

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

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

Summary

This article barely scratches the surface of what is possible with HTML5 Drag and Drop. My goal was to provide an introduction to the topic that focused on the bare essentials needed to get started. Below are links that provide much more in-depth information on the subject.

Helpful Links for HTML5 Drag and Drop

http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html

http://html5demos.com/drag

http://www.webdesignerdepot.com/2013/08/how-to-use-html5s-drag-and-drop/

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

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

http://tutorials.jenkov.com/html5/drag-and-drop.html

What are the Best Tutorial Videos for Web Performance?

Learn about web performance from the experts. These videos provide a wealth of knowledge about how to make web pages faster.

Everyone knows what happens when a page loads slowly: the visitor leaves. That much is simple. But what is not always so obvious is what to do when you have addressed the “low-hanging fruit.” There are a number of variables at play with regard to page load time and at times it can be a bit tricky to understand how to measure and analyze each one. In these talks, you’ll find discussions that go beyond CSS sprites and domain sharding. Sure, those techniques (and other similar ones) are almost always helpful, but there is a deeper level of understanding required when trying to get page load speed down to a few seconds. I’ve not attempted to list every helpful video out there; there are many. This list includes the videos that I feel provide the most helpful review of topics. In each case, I’ve found the speaker to have not only a super-expert level of knowledge / experience, but an ability to translate their expertise into an enjoyable presentation.


Optimizing networking performance (and HTTP 2.0) – Crash course on web performance (Fluent 2013)

Ilya Grigorik covers a wide range of topics that define the technology of web performance. There is a healthy percentage of time spent on mobile, including some very deep concepts such as radio optimization and periodic transfers (beacons), and how they consume more battery life than you might expect. One area that is particularly interesting is the TCP/IP “slow start.” Here Ilya explains why the slow start exists (i.e. it’s actually a feature, not a bug), and how it affects performance.


Bandwidth, latency, and radio performance – Crash course on web performance (Fluent 2013)

If you think you understand what happens when you open a web page on your mobile device, think again. Ilya Grigorik goes into incredible detail when explaining what happens in the life of a mobile web request. A highlight is a very helpful explanation of the difference between bandwidth and latency. In addition, he discusses the psychological / business ramifications of delays of more than 300ms. This is a super in-depth talk and very much worth the time if you are interested in the more subtle areas of web performance.


Steve Souders: High Performance Mobile

Web performance guru Steve Souders give a talk that is still every bit as relevant as it was in in 2011. In addition to talking about his famous “14 Rules for Web Performance,” Steve discusses the prevalence of re-directs, domain sharding and the implications of slow JavaScript. In addition, there are discussions about responsive images, app cache, leveraging async JavaScript calls, and downloading JavaScript without executing it (eval alert! : – )


Steve Souders on Web Performance: How Fast Are We Going Now?

One area of this talk that I find interesting is where Steve talks about barackobama.com and how when they improved the speed of the site by 60%, they found that there was a 14% increase in donation conversions. He also talks about trends in connection speeds, performance differences between browsers (including how JavaScript affects this), and his work with HTTP Archive.


Web Performance Testing at YouTube

Rick Viscomi’s high-level discussion or Real User Monitoring (RUM), and Synthetic Testing is fantastic. As a UX engineer at Google/YouTube, his expertise and insights on performance is extremely helpful. Rick explains Google’s approach to client-side instrumentation (CSI), and how their “measure / beacon / analyze” approach works. He also talks about how Google uses WebPageTest for synthetic testing.


Speed Up Your JavaScript

Some may feel that web performance is only about reducing HTTP requests and leveraging CSS sprites, but when you consider that when any external JavaScript file referenced by a script tag is downloaded and parsed, the page is completely blocked. Although this video is more than five years old, it is a must for any JavaScript developer. Nicholas Zakas talks about Scope management, Data access, loops and DOM access in the context of performance. He also does a deep-dive into what actually happens when a function executes and how that affects performance, how the “with” statement and “catch” clause of the try-catch statement affect the scope chain, as well as performance implications of “for”, “while” and “do while.” There is also a discussion of the tricky behavior of HTMLCollection objects, eflow events, and how the DocumentFragment object can help improve performance.


Mobile Web Performance

Guy Podjarny, CTO of Web & Mobile at Akamai gives a great talk about mobile web performance. This video is nearly three years old, but still relevant. Guy’s perspective is particularly helpful because of his work in Akamai and the experience in that domain.


JavaScript: WAT!

JavaScript LogoIf you think JavaScript is an odd language, just wait: a few experiments with the addition operator will really leave you scratching your head.

Every now and then, I am reminded that as corny as the term “web surfing” may sound, it is sometimes an amazing experience. Recently, while digressing from a sub-reference of a side-topic that was tangentially related to a random blog post I stumbled across while procrastinating, I found a video titled: “WAT”. If you have even the slightest appreciation for, or hatred of JavaScript, it is a hilarious four minutes.

SPOILER ALERT: Try to watch the video before going any further. It’s much more enjoyable if you don’t know what is coming.

Now that you have (hopefully) watched the video:

I have no idea who Gary Bernhardt is, but he makes some interesting points. Here are a few highlights:

QUESTION: What is the return value of this expression? [] + [];

ANSWER: An empty string

QUESTION: What is the return value of this expression? [] + {};

ANSWER: “[object Object]”

QUESTION: What is the return value of this expression? {} + [];

ANSWER: 0

There are a few more examples, but the overall message is: the JavaScript addition operator produces very odd results in corner cases. I must admit, I’ve never thought to explore this behavior and not only were my answers to all three of the above questions wrong, but I was pretty shocked by the correct answers.

Inspired, I decided to try a few of my own.

Array + Object

While [] + {} does return “[object Object]”, that return value is not an object, but simply a string whose value is: “[object Object]”. To prove this, I did the following:

Array + Function

The return value of this is the exact same thing as foo.toString();

Object + Function

As I tried yet another combination, I started to notice a pattern. When using the typeof operator, the return value was a concatenation of the “toString” methods for each value that was being added. So, if the expression is: {} + foo(), the result is “object” and “true” combined, which is: “objecttrue“. ok, got it.

But the fact that foo + {} returns NaN, makes no sense to me. And then there are a few more adventurous roads one might waddle down:

OK Kevin, so what’s your point?

That’s a fair question. Ultimately, since we never do those kinds of things in our JavaScript code (right? right? : – ), none of this should matter. But as I played around with these examples, two important things came to mind:

Troubleshooting

If you ever replicate these kinds of patterns by accident, it results in the kind of hair-pulling frustration that makes you start to actually believe that there is a dark lord of JavaScript controlling your browser. When trying to track down what seems like unexplainable behavior in your code that is clearly a bug, in addition to that missing semi-colon, implied global, or accidental use of “=” instead of “===”, consider these kinds of patterns. While they all seem very unlikely, typos can happen, and are sometimes hard to spot.

A Deeper Understanding

JavaScript is a truly odd specification. While there are plenty of odd things about it that we do know, there are always a few strange patterns like these that one might not have come across yet. There may or may not be anything useful about all of this, but as JavaScript’s ubiquity and maturity continue, any deep understanding of its quirks and idiosyncrasies can only benefit the developer.

VIDEO LINKS

Jump to: 1:22 for the good parts

(If this video link ever stops working, just google: “A lightning talk by Gary Bernhardt from CodeMash 2012”. There are many pages out there that feature this video)

https://www.youtube.com/watch?v=Othc45WPBhA

https://www.destroyallsoftware.com/talks/wat