ddp

drag-drop-polyfill

A drop-in shim to allow you to use existing html5 drag'n'drop code with mobile browsers

Showing:

Popularity

Downloads/wk

51

GitHub Stars

521

Maintenance

Last Commit

2yrs ago

Contributors

31

Package

Dependencies

0

License

MIT

Type Definitions

Built-In

Tree-Shakeable

No?

Readme

dependencies devdependencies npmversion bowerversion license issues size gzippedsize

Polyfill for HTML 5 drag'n'drop

The HTML 5 drag'n'drop API allows you to implement drag'n'drop on most desktop browsers.

Unfortunately, you'll notice most mobile browsers don't support it, so no iPad (or Nexus) action for you!

Luckily, browsers give us enough tools to make it happen ourselves. If you drop this package in your page your existing HTML 5 drag'n'drop code should just work (*almost).

Demos

Demo

Check out the demo to see it in action and monitor the console to see the events firing.

Install

npm

npm install mobile-drag-drop --save

jspm

jspm install npm:mobile-drag-drop

bower

bower install mobile-drag-drop --save

Include

global

<link rel="stylesheet" href="libs/mobile-drag-drop/release/default.css">
<script src="libs/mobile-drag-drop/release/index.min.js"></script>

<!--optional import of scroll behaviour-->
<script src="libs/mobile-drag-drop/release/scroll-behaviour.min.js"></script>

<script>
    // options are optional ;)
    MobileDragDrop.polyfill({
        // use this to make use of the scroll behaviour
        dragImageTranslateOverride: MobileDragDrop.scrollBehaviourDragImageTranslateOverride
    });
</script>

SystemJS/JSPM

System.import("mobile-drag-drop");
// import css if using system-js css loader plugin 
System.import("mobile-drag-drop/default.css!");

ES2015/TypeScript/webpack

import {polyfill} from "mobile-drag-drop";

// optional import of scroll behaviour
import {scrollBehaviourDragImageTranslateOverride} from "mobile-drag-drop/scroll-behaviour";

// options are optional ;)
polyfill({
    // use this to make use of the scroll behaviour
    dragImageTranslateOverride: scrollBehaviourDragImageTranslateOverride
});

Make sure to implement a dragenter-listener! (read here why)

// dragenter listener
(event)=> {
    event.preventDefault();
}

If you're targeting iOS Safari 10.x and higher

window.addEventListener( 'touchmove', function() {});

See #77 for details.

webpack/scss

@import "~mobile-drag-drop/default.css";

API & Options

export interface Point {
    x: number;
    y: number;
}

// function signature for the dragImageTranslateOverride hook
export type DragImageTranslateOverrideFn = (
    // corresponding touchmove event
    event: TouchEvent, 
    // the processed touch event viewport coordinates
    hoverCoordinates: Point, 
    // the element under the calculated touch coordinates
    hoveredElement: HTMLElement, 
    // callback for updating the drag image offset
    translateDragImageFn: (offsetX: number, offsetY: number) => void;
) => void;

export interface Config {

    // flag to force the polyfill being applied and not rely on internal feature detection
    forceApply?:boolean;

    // useful for when you want the default drag image but still want to apply
    // some static offset from touch coordinates to drag image coordinates
    // defaults to (0,0)
    dragImageOffset?:Point;

    // if the dragImage shall be centered on the touch coordinates
    // defaults to false
    dragImageCenterOnTouch?:boolean;

    // the drag and drop operation involves some processing. here you can specify in what interval this processing takes place.
    // defaults to 150ms
    iterationInterval?:number;

    // hook for custom logic that decides if a drag operation should start
    dragStartConditionOverride?:( event:TouchEvent ) => boolean;

    // hook for custom logic that can manipulate the drag image translate offset
    dragImageTranslateOverride?:DragImageTranslateOverrideFn;

    // hook for custom logic that can override the default action based on the original touch event when the drag never started
    // be sure to call event.preventDefault() if handling the default action in the override to prevent the browser default.
    defaultActionOverride?:( event:TouchEvent ) => void;

    // Drag action delay on touch devices ("hold to drag" functionality, useful for scrolling draggable items). Defaults to no delay.
    holdToDrag?:number;

    /**
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * THE FOLLOWING OPTIONS ARE ONLY AVAILABLE IN v2.3.0-rc.0
     *
     */

    // function invoked for each touchstart event to determine if and which touched element is detected as "draggable"
    tryFindDraggableTarget?:( event:TouchEvent ) => HTMLElement | undefined;

    // function implementing how a copy of the dragged element is created
    // NOTE! this function is for customizing HOW an element is transformed to a drag image element
    // if you're looking for setting a custom drag image please use [setDragImage()](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage)
    dragImageSetup?:( element:HTMLElement ) => HTMLElement;

    // function for determining element that is currently hovered while dragging
    // defaults to `document.elementFromPoint()`
    elementFromPoint?:( x:number, y:number ) => Element;

    /**
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     */
}

// invoke for initializing the polyfill => returns true if polyfill is applied
export function polyfill(override?: Config):boolean;

Custom events

When setting the option holdToDrag the draggable element will emit custom events:

  • dnd-poly-dragstart-pending as soon as the touchstart event is detected and a drag operation is about to be started after the delay specified with holdToDrag
  • dnd-poly-dragstart-cancel when the drag operation will not be started due to touchmove, touchend, touchcancel or scroll within the holdToDrag delay.

Those events can be used to visualize the holdToDrag so the user is informed that a drag operation is about to start.

DragImage Customization

If you want to set a custom drag image use setDragImage().

Override the classes that are applied by the polyfill for customizing the drag image appearance and snapback behaviour. Mind the !important.

.dnd-poly-drag-image {
    opacity: .5 !important;
}
/* applied when the drag effect is none and the operation ends */
.dnd-poly-drag-image.snapback {
    -webkit-transition: -webkit-transform 250ms ease-out !important;
    -moz-transition: -moz-transform 250ms ease-out !important;
    -o-transition: -o-transform 250ms ease-out !important;
    transition: transform 250ms ease-out !important;
}
/* applied always as a base class for drop effect styles */
.dnd-poly-drag-icon {
}

CSS classes are applied to the dragImage-element according to the current drop effect: none, copy, move, link.

There is icons.css which defines default styles and icons. Feel free to use this as a starting point.

<link rel="stylesheet" href="[...]/mobile-drag-drop/icons.css">

Custom drag image setup function

One can also set a custom dragImageSetup() function in the polyfill config. This allows to completely customize the routine used to create a copy of the dragged element.

Checkout the default implementation as a starting point.

Known issues and limitations

  • iFrames are currently not supported. Please see #5 for the current state.

  • ShadowDOM/ShadyDOM are currently not working seamlessly. Please see #115 for the current state.

  • :before/:after css pseudo styles can't be copied to the drag image. By default classes are removed on the drag image recursively to avoid side-effects. You can pass a custom dragImageSetup function in the config.

Contributions welcome!

Browser compatibility

BrowserSupportKnown issues
ChromeNativeNo known issues. More info
FirefoxNativeNo known issues. More info
SafariNativeNo known issues.
OperaNativeSame as Chrome.
Internet Explorer 11NativeNo known issues.
EdgeUnknownUnknown
Mobile Safari (<iOS 10)PolyfillNo known issues.
Mobile Safari (iOS 10)Polyfill#77
Chrome on iOSPolyfillNo known issues.
Chrome on AndroidPolyfillNo known issues.
Chrome on touch devicePolyfillNo known issues. More info
Firefox on touch deviceNativeNo known issues.
Firefox on AndroidPolyfillNo known issues.
Amazon SilkUnknownUnknown
Ubuntu PhonePolyfillNo known issues.
IEMobileNativeUnknown

Chrome: Chrome supports touch devices/events. When run on a desktop touch device like MS Surface it turns on touch events which also disables native drag-and-drop support. Touch events can also be set by a user in chrome://flags to auto, on, off.
There is also a flag for enabling drag-and-drop through touch interaction but only for Windows and the option is off by default. The polyfill still works if this setting is active. We cannot detect if this flag is set so we just stick to applying the polyfill when Chrome is detected with touch events enabled.

Firefox: Touch events can be activated by a user in about:config to 0 (off), 1 (on), 2(auto). As of today (FF39.0) touch behavior is off. When touch events are active drag-and-drop interaction will still work, so no need to polyfill.

Cross-browser differences in HTML5 drag'n'drop API

The drag'n'drop API is not implemented consistently in all browsers. This table is an effort to list all things required to make drag'n'drop work in all browsers and with the polyfill.

Browserdragstartdragdragenddragenterdragoverdragleavedragexit
Firefoxevent.dataTransfer.setData(type, data)effectAllowed,dropEffecteffectAllowed,dropEffect
IE11event.preventDefault() when registered on body
Polyfillevent.preventDefault() or dropzone required

empty cells mean there is nothing special to take into account

Polyfill requires dragenter listener

On desktop browsers if no dragenter-handler is registered the drag-operation is silently allowed. Browsers don't implement dropzone-attribute according to caniuse which is why they allow it by default, which violates the spec.

If a handler is set up it has to call event.preventDefault() to allow dropping.

This is pretty bad for the polyfill since JS doesn't allow to check how many listeners were invoked when the event is dispatched, which forces the polyfill to rely on a listener being present calling event.preventDefault() to make it work.

Further notices:

  • FF: If effectAllowed or dropEffect is set in dragstart then dragenter/dragover also need to set it.
  • When using a MS Surface tablet a drag-operation is initiated by touch and hold on a draggable.
  • IE11 and Chrome scroll automatically when dragging close to a viewport edge.

Baseline recommendations for cross-browser/-platform support:

  • Always set drag data on dragstart by calling event.dataTransfer.setData(type, data).
  • Always handle dragenter-event on possible dropzones if the drop is allowed by calling event.preventDefault().
  • Handle dragover-event on dropzone when the drop is allowed by calling event.preventDefault(), otherwise the drag-operation is aborted.

Contribute

Contributions are welcome.

For more details on development setup see CONTRIBUTING.md

Thanks

To the amazing contributors who've provided massive extensions and fixes to the original.

@rem - who created the original demo used to demo this shim's drop-in nature.

License

MIT License

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100
No reviews found
Be the first to rate

Alternatives

gridstackBuild interactive dashboards in minutes.
User Rating
4.5/ 5
2
Top Feedback
1Great Documentation
1Highly Customizable
1Hard to Use
GitHub Stars
4K
Weekly Downloads
9K
dra
dragula:ok_hand: Drag and drop so simple it hurts
User Rating
4.3/ 5
23
Top Feedback
1Buggy
1Abandoned
GitHub Stars
21K
Weekly Downloads
30K
fileapiFileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
User Rating
5.0/ 5
1
Top Feedback
1Great Documentation
1Easy to Use
GitHub Stars
4K
Weekly Downloads
710
sortablejsReorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required.
User Rating
4.8/ 5
28
Top Feedback
3Easy to Use
2Great Documentation
2Performant
GitHub Stars
23K
Weekly Downloads
149K
int
interactjsJavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)
User Rating
5.0/ 5
1
Top Feedback
1Easy to Use
GitHub Stars
10K
Weekly Downloads
17K
See 9 Alternatives

Tutorials

No tutorials found
Add a tutorial