DragHelper
Intro
A drag drop helper class which lets you move elements in page. It supports:
- Dragging the actual element
- Dragging a cloned version of the element
- Dragging extra
relatedElementsalong with the main element - Firing useful events beforeDragStart, dragStart, drag, drop, abort
- Validation by setting a
validBoolean on the drag context object provided to event listeners - Aborting drag with ESCAPE key
- Constraining drag to be only horizontal or vertical using lockX and lockY
- Defining X / Y boundaries using minX, maxX and minY, maxY
- Async finalization (e.g. to show confirmation prompts)
- Animated final transition after mouse up of a valid drop (see animateProxyTo)
- Animated abort transition after an invalid or aborted drop
const buttonContainer = DomHelper.createElement({ id : 'buttonContainer', parent : targetElement, style : { display : 'flex', gap : '1em' } }); const toolbar = new Toolbar({ insertFirst : targetElement, style : 'margin-bottom: 2em;width:40em', items : { button : { type : 'button', disabled : true, text : 'Drag buttons here' } } }); new Button({ appendTo : buttonContainer, rendition : 'filled', text : 'Drag me', icon : 'fa fa-tree', onClick : ({ source: button }) => Toast.show('Merry Xmas') }); new Button({ appendTo : buttonContainer, rendition : 'filled', text : 'Drag me to toolbar', icon : 'fa fa-globe', onClick : ({ source: button }) => Toast.show('Hello World') }); const dragHelper = new DragHelper({ outerElement : targetElement, // Only allow drag of buttons inside the fiddle targetSelector : '#buttonContainer .b-button', // Only allow drops on the toolbar dropTargetSelector : '.b-toolbar', callOnFunctions : true, onDragStart() { // Highlight the drop target area toolbar.element.style.outline = '1px dashed #aaa'; }, async onDrop({ context, event }) { // Clear the highlight toolbar.element.style.outline = ''; if (context.valid) { const button = Widget.fromElement(context.grabbed); if (toolbar.widgetMap.helpLabel) { toolbar.remove(toolbar.widgetMap.helpLabel); } await this.animateProxyTo(toolbar.contentElement.lastElementChild || Rectangle.content(toolbar.contentElement), { align : toolbar.contentElement.lastElementChild ? 'l-r' : 'l-l', // Some offset to account for the margin between buttons offset : toolbar.contentElement.lastElementChild ? [10, 0] : null }); toolbar.add(button); Toast.show('👍 Nice drop!'); } } }); fiddle.on('destroy', () => dragHelper.destroy()); Two modes
DragHelper supports two modes:
container- moving / rearranging elements within and between specified containerstranslateXY- freely repositioning an element, either using the element or a cloned version of it - a "drag proxy" (default mode)
Container drag mode
Container drag should be used when moving or rearranging child elements within and between specified containers
Example:
// dragging element between containers
let dragHelper = new DragHelper({
mode : 'container',
containers : [ container1, container2 ]
});
Translate drag mode
Use translate drag to reposition an element within its container using translate CSS.
Example:
// dragging element within container
let dragHelper = new DragHelper({
mode : 'translateXY',
targetSelector : 'div.movable'
});
Observable events
In the various events fired by the DragHelper, you will have access to the raw DOM event and some useful context about the drag operation:
myDrag.on({
drag : ({event , context}) {
// The element which we're moving, could be a cloned version of grabbed, or the grabbed element itself
const element = context.element;
// The original mousedown element upon which triggered the drag operation
const grabbed = context.grabbed;
// The target under the current mouse / pointer / touch position
const target = context.target;
}
});
Simple drag helper subclass with a drop target specified:
export default class MyDrag extends DragHelper {
static configurable = {
// Don't drag the actual cell element, clone it
cloneTarget : true,
mode : 'translateXY',
// Only allow drops on DOM elements with 'yourDropTarget' CSS class specified
dropTargetSelector : '.yourDropTarget',
// Only allow dragging elements with the 'draggable' CSS class
targetSelector : '.draggable'
}
construct(config) {
const me = this;
super.construct(config);
me.on({
dragstart : me.onDragStart
});
}
onDragStart({ event, context }) {
const target = context.target;
// Here you identify what you are dragging (an image of a user, grid row in an order table etc) and map it to something in your
// data model. You can store your data on the context object which is available to you in all drag-related events
context.userId = target.dataset.userId;
}
onEquipmentDrop({ context, event }) {
const me = this;
if (context.valid) {
const userId = context.userId,
droppedOnTarget = context.target;
console.log(`You dropped user ${userStore.getById(userId).name} on ${droppedOnTarget}`, droppedOnTarget);
// Dropped on a scheduled event, display toast
Toast.show(`You dropped user ${userStore.getById(userId).name} on ${droppedOnTarget}`);
}
}
};
Dragging multiple elements
You can tell the DragHelper to also move additional relatedElements when a drag operation is starting. Simply provide an array of elements on the context object:
new DragHelper ({
callOnFunctions : true,
onDragStart({ context }) {
// Let drag helper know about extra elements to drag
context.relatedElements = Array.from(element.querySelectorAll('.b-resource-avatar'));
}
});
Creating a custom drag proxy
Using the createProxy you can create any markup structure to use when dragging cloned targets.
new DragHelper ({
callOnFunctions : true,
// Don't drag the actual cell element, clone it
cloneTarget : true,
// We size the cloned element using CSS
autoSizeClonedTarget : false,
mode : 'translateXY',
// Only allow drops on certain DOM nodes
dropTargetSelector : '.myDropTarget',
// Only allow dragging cell elements in a Bryntum Grid
targetSelector : '.b-grid-row:not(.b-group-row) .b-grid-cell'
// Here we receive the element where the drag originated and we can choose to return just a child element of it
// to use for the drag proxy (such as an icon)
createProxy(element) {
return element.querySelector('i').cloneNode();
}
});
Animating a cloned drag proxy to a point before finalizing
To provide users with the optimal user experience, you can set a transitionTo object (with target element and align spec) on the DragHelper´s context object inside a drop listener (only applies to translate mode operations). This will trigger a final animation of the drag proxy which should represent the change of data state that will be triggered by the drop.
You can see this in action in Gantt´s drag-resource-from-grid demo.
new DragHelper ({
callOnFunctions : true,
// Don't drag the actual cell element, clone it
cloneTarget : true,
// We size the cloned element using CSS
autoSizeClonedTarget : false,
mode : 'translateXY',
// Only allow drops on certain DOM nodes
dropTargetSelector : '.myDropTarget',
// Only allow dragging cell elements in a Bryntum Grid
targetSelector : '.b-grid-row:not(.b-group-row) .b-grid-cell'
// Here we receive the element where the drag originated and we can choose to return just a child element of it
// to use for the drag proxy (such as an icon)
createProxy(element) {
return element.querySelector('i').cloneNode();
},
async onDrop({ context, event }) {
// If it's a valid drop, provide a point to animate the proxy to before finishing the operation
if (context.valid) {
await this.animateProxyTo(someElement, {
// align left side of drag proxy to right side of the someElement
align : 'l0-r0'
});
}
else {
Toast.show(`You cannot drop here`);
}
}
});
Useful configs and properties
| Config | Description |
|---|---|
| mode | Drag mode: container or translateXY |
| targetSelector | CSS selector to match draggable elements |
| dropTargetSelector | CSS selector for valid drop targets |
| cloneTarget | Clone the element instead of moving it |
| lockX | Lock horizontal movement |
| lockY | Lock vertical movement |
See also
- DomHelper - DOM element utilities
- EventHelper - DOM event utilities
Configs
Configs are options you supply in a configuration object when creating an instance of this class-
Set to
falseto not apply width/height of cloned drag proxy elements. -
Set to
trueto clone the dragged target, and not move the actual target DOM node. -
Constrain translate drag to dragWithin elements bounds (set to
falseto allow it to "overlap" edges) -
Containers whose elements can be rearranged (and moved between the containers). Used when mode is set to "container".
-
Drag proxy CSS class
-
The amount of pixels to move mouse before it counts as a drag operation
-
Outer element that limits where element can be dragged
-
CSS class added to the source element in Container drag
-
CSS class added to the source element in Container drag
-
A CSS selector added to each drop target element while dragging.
-
A CSS selector used to determine if a drop is allowed at the current position.
-
Set to
trueto hide the original element while dragging (applicable whencloneTargetis true). -
A CSS selector used to exclude elements when using container mode
-
CSS class added when drag is invalid
-
Configure as
trueto disallow dragging in theXaxis. The dragged element will only move vertically. -
Configure as
trueto disallow dragging in theYaxis. The dragged element will only move horizontally. -
Largest allowed x when dragging horizontally.
-
Largest allowed y when dragging horizontally.
-
Smallest allowed x when dragging horizontally.
-
Smallest allowed y when dragging horizontally.
-
Enabled dragging, specify mode:
container Allows reordering elements within one and/or between multiple containers translateXY Allows dragging within a parent container -
The outer element where the drag helper will operate (attach events to it and use as outer limit when looking for ancestors)
-
A CSS selector used to target a child element of the mouse down element, to use as the drag proxy element. Applies to translate mode when using cloneTarget.
-
Configure as
falseto take ownership of the proxy element after a valid drop (advanced usage). -
Scroll manager of the target. If specified, scrolling while dragging is supported.
-
A method provided to snap coordinates to fixed points as you drag
-
A Widget or HTML element to drag. See also targetSelector.
-
A CSS selector used to target draggable elements. See also target.
-
The amount of milliseconds to wait after a touchstart, before a drag gesture will be allowed to start.
-
When using unifiedProxy, use this amount of pixels to offset each extra element when dragging multiple items
-
Set to
trueto stack any related dragged elements below the main drag proxy element. Only applicable when using translate mode with cloneTarget -
Internal listeners, that cannot be removed by the user.
Properties
Properties are getters/setters or publicly accessible variables on this class-
Identifies an object as an instance of DragHelper class, or subclass thereof.
-
Identifies an object as an instance of Events class, or subclass thereof.
-
A class property getter for the default values of internal properties for this class.
-
Returns true if a drag operation is active
-
An empty array that can be used as a default value.
-
An empty object that can be used as a default value.
-
Identifies an object as an instance of DragHelper class, or subclass thereof.
-
Returns a copy of the full configuration which was used to configure this object.
-
This property is set to
truebefore theconstructorreturns. -
This property is set to
trueon entry to the destroy method. It remains on the objects after returning fromdestroy(). If isDestroyed istrue, this property will also betrue, so there is no need to test for both (for example,comp.isDestroying || comp.isDestroyed).
Functions
Functions are methods available for calling on the class-
This optional class method is called when a class is mixed in using the mixin() method.
-
Registers this class type with its Factory
-
initListeners( )private
Initialize listener
-
update( )private
Updates drag, called when an element is grabbed and mouse moves
Triggers: drag
-
Internal function used to hook destroy() calls when using thisObj
-
Internal function used restore hooked destroy() calls when using thisObj
-
Auto detaches listeners registered from start, if set as detachable
-
Internal function used to run a callback function after an event is triggered
-
Removes all listeners registered to this object by the application.