I\'m trying to use the HTML5 draggable API (though I realize it has its problems). So far, the only showstopper I\'ve encountered is that I can\'t figure out a way to determ
A (very inelegant) solution is to store a selector as a type of data in the dataTransfer
object. Here is an example: http://jsfiddle.net/TrevorBurnham/eKHap/
The active lines here are
e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');
Then in the dragover
and dragenter
events, e.dataTransfer.types
contains the string 'draggable'
, which is the ID needed to determine which element is being dragged. (Note that browsers apparently require data to be set for a recognized MIME type like text/html
as well in order for this to work. Tested in Chrome and Firefox.)
It's an ugly, ugly hack, and if someone can give me a better solution, I'll happily grant them the bounty.
Update: One caveat worth adding is that, in addition to being inelegant, the spec states that all data types will be converted to lower-case ASCII. So be warned that selectors involving capital letters or unicode will break. Jeffery's solution sidesteps this issue.
In the drag
event, copy event.x
and event.y
to an object and set it as the value of an expando property on the dragged element.
function drag(e) {
this.draggingAt = { x: e.x, y: e.y };
}
In the dragenter
and dragleave
events find the element whose expando property value matches the event.x
and event.y
of the current event.
function dragEnter(e) {
var draggedElement = dragging.filter(function(el) {
return el.draggingAt.x == e.x && el.draggingAt.y == e.y;
})[0];
}
To reduce the number of elements you need to look at, you can keep track of elements by adding them to an array or assigning a class in the dragstart
event, and undoing that in the dragend
event.
var dragging = [];
function dragStart(e) {
e.dataTransfer.setData('text/html', '');
dragging.push(this);
}
function dragEnd(e) {
dragging.splice(dragging.indexOf(this), 1);
}
http://jsfiddle.net/gilly3/4bVhL/
Now, in theory this should work. However, I don't know how to enable dragging for a touch device, so I wasn't able to test it. This link is mobile formatted, but touch and slide didn't cause dragging to start on my android. http://fiddle.jshell.net/gilly3/4bVhL/1/show/
Edit: From what I've read, it doesn't look like HTML5 draggable is supported on any touch devices. Are you able to get draggable working on any touch devices? If not, multi-touch wouldn't be an issue and you can resort to just storing the dragged element in a variable.
The short answer to my question turns out to be: No. The WHATWG spec doesn't provide a reference to the element being dragged (called the "source node" in the spec) in the dragenter
, dragover
, or dragleave
events.
Why not? Two reasons:
First, as Jeffery points out in his comment, the WHATWG spec is based on IE5+'s implementation of drag-and-drop, which predated multi-touch devices. (As of this writing, no major multi-touch browser implements HTML drag-and-drop.) In a "single-touch" context, it's easy to store a global reference to the current dragged element on dragstart
.
Second, HTML drag-and-drop allows you to drag elements across multiple documents. This is awesome, but it also means that providing a reference to the element being dragged in every dragenter
, dragover
, or dragleave
event wouldn't make sense; you can't reference an element in a different document. It's a strength of the API that those events work the same way whether the drag originated in the same document or a different one.
But the inability to provide serialized information to all drag events, except through dataTransfer.types
(as described in my working solution answer), is a glaring omission in the API. I've submitted a proposal for public data in drag events to the WHATWG, and I hope you'll express your support.