The Problem
The HTML5 specification includes support for drag and drop operations.
This is supported by all modern desktop browsers, but not by mobile browsers running under Android and IOS. This is a problem because one of the main reasons to develop applications using HTML5 is the ability to run on all devices.
I suspect mobile browsers don't support drag and drop for two reasons:
- Their screens are often too small for useful drag/drop operations.
- They use touch for other tasks such as scrolling and zooming, which could interfere with D&D.
But many mobile devices have screens that are large enough. This includes pretty much all tablets and a lot of phones. And not being able to scroll or zoom by dragging some elements (those with a draggable attribute) in exchange for drag/drop support seems like a reasonable trade-off.
Therefore, in order to use drag and drop on mobile devices today, you have two options:
- Forget about the HTML5 drag and drop APIs and use a custom drag-drop library (your own, or one of several existing options); or
- Use a polyfill that translates touch events into HTML5 drag and drop events.
We prefer the second approach because standards are important. Following the HTML drag and drop standard instead of using a custom library means your existing drag and drop code will work with standards-based frameworks and components without significant changes.
For example, several Wijmo controls use HTML5 drag and drop extensively:
- The FlexGrid uses it to re-arrange and resize rows and columns.
- The GroupPanel uses it to re-arrange and sort groups.
- The PivotPanel uses it to build OLAP views and to provide context-menus.
Unfortunately, until recently all this functionality was limited or non-existent when running on mobile devices. You could drag and drop grid columns, groups, and pivot fields with the mouse, but not using touch.
The Solution
When our customers told us they wanted support for drag and drop operations on mobile devices, we decided to follow the polyfill approach, and implemented a DragDropTouch class that translates touch events into HTML5 drag and drop events transparently. This allowed us to improve touch support for all our controls at once, without having to change any of the controls.
The DragDropTouch polyfill works as follows:
- It attaches listeners to touch events.
- On touchstart, it checks whether the target element has the draggable attribute or is contained in an element that does. If that is the case, it saves a reference to this “drag source” element and prevents the default handling of the event.
- On touchmove, it checks whether the touch has moved a certain threshold distance from the origin. If that is the case, it raises the dragstart event and continues monitoring moves to fire dragenter and dragleave.
- On touchend, it raises the dragend and drop events.
This is enough to handle drag and drop, but is not enough to fully support touch. The problem is that once a touch is detected on a draggable element, the default handling of the event must be prevented in order to prevent scrolling. But not all touches are meant to start drag operations. The user may simply want to click/tap the element, or double-click it, or display a context menu.
In other words, it is not enough to provide drag and drop support for touch actions; the polyfill must not interfere when the touch is not a drag and drop action.
Because of this requirement, the polyfill must perform a few additional tasks:
- Raise the mousemove, mousedown, mouseup, and click events when the user touches a draggable element but doesn’t start dragging,
- Raise the dblclick event when there's a new touchstart right after a click, and
- Raise the contextmenu event when the touch lasts a while but the user doesn’t start dragging the element.
That’s all there is to it.
Using the polyfill is easy. All you have to do is add this script tag to your pages:
<script src="scripts/DragDropTouch.js"></script>
The script will automatically create an instance of the DragDropTouch class and will start translating the events, enabling drag and drop operations to work on Android and IOS devices the same way they work on desktop browsers.
We have created a sample that demonstrates the effect of the polyfill using a classic HTML5 drag drop sample as well as some Wijmo controls. You can see the sample in action here:
http://bernardo-castilho.github.io/DragDropTouch/demo/
The picture below shows the sample running on an iPad while the user drags element A to a new position:
Conclusion
Implementing a polyfill to translate touch actions into drag-drop events is an efficient way to improve the usability of standards-based components so they support drag and drop actions on mobile devices.
The polyfill described above extends mouse support to several Wijmo controls that support drag and drop operations, but it is generic, so it will work with other standards-based components and applications as well.
All you have to do is add one script tag to your application, open it on your iPad, and drag away!
This article contains a lot of interesting information on different types of pointer events, including mouse, touch, and the proposed standard for single set of “pointer events”:
http://www.html5rocks.com/en/mobile/touchandmouse/
And this article focuses specifically on HTML5 drag and drop:
http://www.html5rocks.com/en/tutorials/dnd/basics/