HTML Drag and drop



To drag data into and out of an HTML-based application (or into and out of the HTML displayed in an HTMLLoader), you can use HTML drag and drop events. The HTML drag-and-drop API allows you to drag to and from DOM elements in the HTML content.

Note: You can also use the AIR NativeDragEvent and NativeDragManager APIs by listening for events on the HTMLLoader object containing the HTML content. However, the HTML API is better integrated with the HTML DOM and gives you control of the default behavior.

Default drag-and-drop behavior

THe HTML environment provides default behavior for drag-and-drop gestures involving text, images, and URLs. Using the default behavior, you can always drag these types of data out of an element. However, you can only drag text into an element and only to elements in an editable region of a page. When you drag text between or within editable regions of a page, the default behavior performs a move action. When you drag text to an editable region from a non-editable region or from outside the application, then the default behavior performs a copy action.

You can override the default behavior by handling the drag-and-drop events yourself. To cancel the default behavior, you must call the preventDefault() methods of the objects dispatched for the drag-and-drop events. You can then insert data into the drop target and remove data from the drag source as necessary to perform the chosen action.

By default, the user can select and drag any text, and drag images and links. You can use the WebKit CSS property, -webkit-user-select to control how any HTML element can be selected. For example, if you set -webkit-user-select to none, then the element contents are not selectable and so cannot be dragged. You can also use the -webkit-user-drag CSS property to control whether an element as a whole can be dragged. However, the contents of the element are treated separately. The user could still drag a selected portion of the text. For more information, see Extensions to CSS.

Drag-and-drop events in HTML

The events dispatched by the initiator element from which a drag originates, are:

Event

Description

dragstart

Dispatched when the user starts the drag gesture. The handler for this event can prevent the drag, if necessary, by calling the preventDefault() method of the event object. To control whether the dragged data can be copied, linked, or moved, set the effectAllowed property. Selected text, images, and links are put onto the clipboard by the default behavior, but you can set different data for the drag gesture using the dataTransfer property of the event object.

drag

Dispatched continuously during the drag gesture.

dragend

Dispatched when the user releases the mouse button to end the drag gesture.

The events dispatched by a drag target are:

Event

Description

dragover

Dispatched continuously while the drag gesture remains within the element boundaries. The handler for this event should set the dataTransfer.dropEffect property to indicate whether the drop will result in a copy, move, or link action if the user releases the mouse.

dragenter

Dispatched when the drag gesture enters the boundaries of the element.

If you change any properties of a dataTransfer object in a dragenter event handler, those changes are quickly overridden by the next dragover event. On the other hand, there is a short delay between a dragenter and the first dragover event that can cause the cursor to flash if different properties are set. In many cases, you can use the same event handler for both events.

dragleave

Dispatched when the drag gesture leaves the element boundaries.

drop

Dispatched when the user drops the data onto the element. The data being dragged can only be accessed within the handler for this event.

The event object dispatched in response to these events is similar to a mouse event. You can use mouse event properties such as (clientX, clientY) and (screenX, screenY), to determine the mouse position.

The most important property of a drag event object is dataTransfer, which contains the data being dragged. The dataTransfer object itself has the following properties and methods:

Property or Method

Description

effectAllowed

The effect allowed by the source of the drag. Typically, the handler for the dragstart event sets this value. See Drag effects in HTML.

dropEffect

The effect chosen by the target or the user. If you set the dropEffect in a dragover or dragenter event handler, then AIR updates the mouse cursor to indicate the effect that occurs if the user releases the mouse. If the dropEffect set does not match one of the allowed effects, no drop is allowed and the unavailable cursor is displayed. If you have not set a dropEffect in response to the latest dragover or dragenter event, then the user can choose from the allowed effects with the standard operating system modifier keys.

The final effect is reported by the dropEffect property of the object dispatched for dragend. If the user abandons the drop by releasing the mouse outside an eligible target, then dropEffect is set to none.

types

An array containing the MIME type strings for each data format present in the dataTransfer object.

getData(mimeType)

Gets the data in the format specified by the mimeType parameter.

The getData() method can only be called in response to the drop event.

setData(mimeType)

Adds data to the dataTransfer in the format specified by the mimeType parameter. You can add data in multiple formats by calling setData() for each MIME type. Any data placed in the dataTransfer object by the default drag behavior is cleared.

The setData() method can only be called in response to the dragstart event.

clearData(mimeType)

Clears any data in the format specified by the mimeType parameter.

setDragImage(image, offsetX, offsetY)

Sets a custom drag image. The setDragImage() method can only be called in response to the dragstart event and only when an entire HTML element is dragged by setting its -webkit-user-drag CSS style to element. The image parameter can be a JavaScript Element or Image object.

MIME types for the HTML drag-and-drop

The MIME types to use with the dataTransfer object of an HTML drag-and-drop event include:

Data format

MIME type

Text

"text/plain"

HTML

"text/html"

URL

"text/uri-list"

Bitmap

"image/x-vnd.adobe.air.bitmap"

File list

"application/x-vnd.adobe.air.file-list"

You can also use other MIME strings, including application-defined strings. However, other applications may not be able to recognize or use the transferred data. It is your responsibility to add data to the dataTransfer object in the expected format.

Important: Only code running in the application sandbox can access dropped files. Attempting to read or set any property of a File object within a non-application sandbox generates a security error. See Handling file drops in non-application HTML sandboxes for more information.

Drag effects in HTML

The initiator of the drag gesture can limit the allowed drag effects by setting the dataTransfer.effectAllowed property in the handler for the dragstart event. The following string values can be used:

String value

Description

"none"

No drag operations are allowed.

"copy"

The data will be copied to the destination, leaving the original in place.

"link"

The data will be shared with the drop destination using a link back to the original.

"move”

The data will be copied to the destination and removed from the original location.

"copyLink"

The data can be copied or linked.

"copyMove"

The data can be copied or moved.

"linkMove"

The data can be linked or moved.

"all"

The data can be copied, moved, or linked. All is the default effect when you prevent the default behavior.

The target of the drag gesture can set the dataTransfer.dropEffect property to indicate the action that is taken if the user completes the drop. If the drop effect is one of the allowed actions, then the system displays the appropriate copy, move, or link cursor. If not, then the system displays the unavailable cursor. If no drop effect is set by the target, the user can choose from the allowed actions with the modifier keys.

Set the dropEffect value in the handlers for both the dragover and dragenter events:

function doDragStart(event) { 
    event.dataTransfer.setData("text/plain","Text to drag"); 
    event.dataTransfer.effectAllowed = "copyMove"; 
} 
 
function doDragOver(event) { 
    event.dataTransfer.dropEffect = "copy"; 
} 
 
function doDragEnter(event) { 
    event.dataTransfer.dropEffect = "copy"; 
}
Note: Although you should always set the dropEffect property in the handler for dragenter, be aware that the next dragover event resets the property to its default value. Set dropEffect in response to both events.

Dragging data out of an HTML element

The default behavior allows most content in an HTML page to be copied by dragging. You can control the content allowed to be dragged using CSS properties -webkit-user-select and -webkit-user-drag.

Override the default drag-out behavior in the handler for the dragstart event. Call the setData() method of the dataTransfer property of the event object to put your own data into the drag gesture.

To indicate which drag effects a source object supports when you are not relying on the default behavior, set the dataTransfer.effectAllowed property of the event object dispatched for the dragstart event. You can choose any combination of effects. For example, if a source element supports both copy and link effects, set the property to "copyLink".

Setting the dragged data

Add the data for the drag gesture in the handler for the dragstart event with the dataTransfer property. Use the dataTransfer.setData() method to put data onto the clipboard, passing in the MIME type and the data to transfer.

For example, if you had an image element in your application, with the id imageOfGeorge, you could use the following dragstart event handler. This example adds representations of a picture of George in several data formats, which increases the likelihood that other applications can use the dragged data.

function dragStartHandler(event){         
    event.dataTransfer.effectAllowed = "copy"; 
     
    var dragImage = document.getElementById("imageOfGeorge"); 
    var dragFile = new air.File(dragImage.src); 
    event.dataTransfer.setData("text/plain","A picture of George"); 
    event.dataTransfer.setData("image/x-vnd.adobe.air.bitmap", dragImage); 
    event.dataTransfer.setData("application/x-vnd.adobe.air.file-list", 
                                new Array(dragFile)); 
}
Note: When you call the setData() method of dataTransfer object, no data is added by the default drag-and-drop behavior.

Dragging data into an HTML element

The default behavior only allows text to be dragged into editable regions of the page. You can specify that an element and its children can be made editable by including the contenteditable attribute in the opening tag of the element. You can also make an entire document editable by setting the document object designMode property to "on".

You can support alternate drag-in behavior on a page by handling the dragenter, dragover, and drop events for any elements that can accept dragged data.

Enabling drag-in

To handle the drag-in gesture, you must first cancel the default behavior. Listen for the dragenter and dragover events on any HTML elements you want to use as drop targets. In the handlers for these events, call the preventDefault() method of the dispatched event object. Canceling the default behavior allows non-editable regions to receive a drop.

Getting the dropped data

You can access the dropped data in the handler for the ondrop event:

function doDrop(event){ 
    droppedText = event.dataTransfer.getData("text/plain"); 
}

Use the dataTransfer.getData() method to read the data onto the clipboard, passing in the MIME type of the data format to read. You can find out which data formats are available using the types property of the dataTransfer object. The types array contains the MIME type string of each available format.

When you cancel the default behavior in the dragenter or dragover events, you are responsible for inserting any dropped data into its proper place in the document. No API exists to convert a mouse position into an insertion point within an element. This limitation can make it difficult to implement insertion-type drag gestures.

Example: Overriding the default HTML drag-in behavior

This example implements a drop target that displays a table showing each data format available in the dropped item.

The default behavior is used to allow text, links, and images to be dragged within the application. The example overrides the default drag-in behavior for the div element that serves as the drop target. The key step to enabling non-editable content to accept a drag-in gesture is to call the preventDefault() method of the event object dispatched for both the dragenter and dragover events. In response to a drop event, the handler converts the transferred data into an HTML row element and inserts the row into a table for display.

<html> 
<head> 
<title>Drag-and-drop</title> 
<script language="javascript" type="text/javascript" src="AIRAliases.js"></script> 
<script language="javascript"> 
    function init(){ 
        var target = document.getElementById('target'); 
        target.addEventListener("dragenter", dragEnterOverHandler); 
        target.addEventListener("dragover", dragEnterOverHandler); 
        target.addEventListener("drop", dropHandler); 
         
        var source = document.getElementById('source'); 
        source.addEventListener("dragstart", dragStartHandler); 
        source.addEventListener("dragend", dragEndHandler); 
         
        emptyRow = document.getElementById("emptyTargetRow"); 
    } 
     
    function dragStartHandler(event){         
        event.dataTransfer.effectAllowed = "copy"; 
    } 
 
    function dragEndHandler(event){ 
        air.trace(event.type + ": " + event.dataTransfer.dropEffect); 
    } 
     
    function dragEnterOverHandler(event){ 
        event.preventDefault(); 
    }     
 
    var emptyRow; 
    function dropHandler(event){ 
        for(var prop in event){ 
            air.trace(prop + " = " + event[prop]); 
        } 
        var row = document.createElement('tr'); 
        row.innerHTML = "<td>" + event.dataTransfer.getData("text/plain") + "</td>" + 
            "<td>" + event.dataTransfer.getData("text/html") + "</td>" + 
            "<td>" + event.dataTransfer.getData("text/uri-list") + "</td>" + 
            "<td>" + event.dataTransfer.getData("application/x-vnd.adobe.air.file-list") + 
            "</td>"; 
 
        var imageCell = document.createElement('td'); 
        if((event.dataTransfer.types.toString()).search("image/x-vnd.adobe.air.bitmap") > -1){ 
            imageCell.appendChild(event.dataTransfer.getData("image/x-vnd.adobe.air.bitmap")); 
        } 
        row.appendChild(imageCell); 
        var parent = emptyRow.parentNode; 
        parent.insertBefore(row, emptyRow); 
    } 
</script> 
</head> 
<body onLoad="init()" style="padding:5px">  
<div> 
    <h1>Source</h1> 
       <p>Items to drag:</p> 
    <ul id="source"> 
         <li>Plain text.</li> 
           <li>HTML <b>formatted</b> text.</li> 
           <li>A <a href="http://www.adobe.com">URL.</a></li> 
           <li><img src="icons/AIRApp_16.png" alt="An image"/></li> 
           <li style="-webkit-user-drag:none;"> 
         Uses "-webkit-user-drag:none" style. 
           </li> 
           <li style="-webkit-user-select:none;"> 
         Uses "-webkit-user-select:none" style. 
           </li> 
 
       </ul> 
</div> 
<div id="target" style="border-style:dashed;"> 
    <h1 >Target</h1> 
       <p>Drag items from the source list (or elsewhere).</p> 
       <table id="displayTable" border="1"> 
         <tr><th>Plain text</th><th>Html text</th><th>URL</th><th>File list</th><th>Bitmap Data</th></tr> 
           <tr id="emptyTargetRow"><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr> 
       </table> 
       </div> 
</div> 
</body> 
</html>

Handling file drops in non-application HTML sandboxes

Non-application content cannot access the File objects that result when files are dragged into an AIR application. Nor is it possible to pass one of these File objects to application content through a sandbox bridge. (The object properties must be accessed during serialization.) However, you can still drop files in your application by listening for the AIR nativeDragDrop events on the HTMLLoader object.

Normally, if a user drops a file into a frame that hosts non-application content, the drop event does not propagate from the child to the parent. However, since the events dispatched by the HTMLLoader (which is the container for all HTML content in an AIR application) are not part of the HTML event flow, you can still receive the drop event in application content.

To receive the event for a file drop, the parent document adds an event listener to the HTMLLoader object using the reference provided by window.htmlLoader:

window.htmlLoader.addEventListener("nativeDragDrop",function(event){ 
    var filelist = event.clipboard.getData(air.ClipboardFormats.FILE_LIST_FORMAT); 
    air.trace(filelist[0].url); 
});

The following example uses a parent document that loads a child page into a remote sandbox (http://localhost/). The parent listens for the nativeDragDrop event on the HTMLLoader object and traces out the file url.

<html> 
<head> 
<title>Drag-and-drop in a remote sandbox</title> 
<script language="javascript" type="text/javascript" src="AIRAliases.js"></script> 
<script language="javascript"> 
    window.htmlLoader.addEventListener("nativeDragDrop",function(event){ 
        var filelist = event.clipboard.getData(air.ClipboardFormats.FILE_LIST_FORMAT); 
        air.trace(filelist[0].url); 
    }); 
</script> 
</head> 
<body> 
       <iframe src="child.html" 
               sandboxRoot="http://localhost/"  
               documentRoot="app:/"  
               frameBorder="0"  width="100%" height="100%"> 
       </iframe> 
</body> 
</html>

The child document must present a valid drop target by preventing the Event object preventDefault() method in the HTML dragenter and dragover event handlers or the drop event can never occur.

<html> 
<head> 
    <title>Drag and drop target</title>             
    <script language="javascript" type="text/javascript"> 
        function preventDefault(event){ 
            event.preventDefault(); 
        } 
    </script> 
</head> 
<body ondragenter="preventDefault(event)" ondragover="preventDefault(event)">  
<div> 
<h1>Drop Files Here</h1> 
</div> 
</body> 
</html>

For more information, see Programming in HTML and JavaScript.