setDragImage Edge Cases
What this is
The HTML Drag and Drop API is an important tool for building dynamic interfaces, but it is inconsistently implemented across browsers (which can be quite frustrating). The goal of this post is to enumerate some of these rough edges, specifically with regards to the setDragImage
method on the dataTransfer
object1.
Firefox and the DOM
When using a custom element for the drag image, Firefox prefers it to be in the DOM. In fact, it won’t display it otherwise. Safari and Chrome permit this, but they don’t like it. They’ll often pick up weird artifacts in the drag image from nearby elements.
Unfortunately, there isn’t a good way to test for this other than looking at the browser’s user agent2:
It works like this, on dragstart
add the element to the DOM for Firefox:
And on dragend
clean-up the drag image:
Using an image
To use an image (or background image) for the drag image, it must first be downloaded. Otherwise the drag image appears blank on drag. Images can be prefetched using an image element:
Also worth noting, animated gifs do not animate when set as the drag image.
Positioning the drag image
The latter two arguments for setDragImage
are the x and y offset of the drag image relative to the cursor. Often examples set these to a specific number like 0
, but this differs from the normal img
drag behavior which sets the drag image relative to the cursor position on mousedown.
To recreate this behavior, use getBoundingClientRect()
to calculate the offset position:
Maximum drag image dimensions
Anecdotally these are the largest pixel dimensions for a drag image before cropping occurs:
400×400 - Safari/Chrome
700×450 - Firefox
Noting this, it is important to scale the dimension of the drag image as well as its offset position. The following scales the clientRect
to the smallest maximum:
Firefox and window.devicePixelRatio
Unlike Safari and Chrome, Firefox sees the actual dimensions not the retina-scaled dimensions. Multiply the offsets by the devicePixelRatio
, otherwise Firefox’s offset is always off on retina screens3:
Firefox and the drag event
Safari and Chrome provide dynamic mouse position on the drag
event. Firefox does not (wontfix). To get around this just use the dragover
event which provides the mouse position in all browsers.
Other notes
- Firefox will not allow you to drag an element without the
draggable
attribute. - The drag image element accepts limited styling.
-
setDragImage
is used to set a custom ghosted image that appears while adraggable
element is being dragged. This post glosses over the basics of its usage, which is covered quite thoroughly in Setting a custom ghost image when using HTML5 drag and drop. ↩ -
It would better to have a feature test for this. I’m open to suggestions. ↩
-
Again, open to suggestions for feature detection in lieu of user agent detection. ↩