model/draggable

There is no license information available for the latest version (v0.1.0) of this package.

Javascript utility to enable drag&drop ordering between DOM elements

Maintainers

Package info

github.com/model-composer/draggable

pkg:composer/model/draggable

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-26 14:57 UTC

This package is auto-updated.

Last update: 2026-08-26 14:58:26 UTC


README

Javascript utility to enable drag&drop ordering between DOM elements.

This is the ModEl 4 port of the legacy DraggableOrder module. It ships the CSS/JS as an asset library declared through an AssetsProvider, so model/assets discovers and serves them automatically — no manual file inclusion needed.

Unlike the v3 module, it has no hard dependency on the legacy FrontEnd module: the helpers it used to borrow from it (getMouseCoords, getElementCoords, getMouseCoordsInElement, addClass, removeClass) are now implemented internally or replaced by their native equivalents. onHtmlChange is still used to re-scan dynamically loaded HTML, but only if FrontEnd happens to be loaded.

Installation

composer require model/draggable

Enabling the assets

The library is registered under the name draggable but is not auto-enabled. Enable it (typically from a controller's init) wherever a page uses it:

use Model\Assets\Assets;

Assets::enable('draggable');

This adds style.css (in the head) and js.js (in the foot) to the asset render list.

Usage

Mark the container with data-draggable-cont (its value is the name you will use to read the order back), and its direct children become reorderable by dragging:

<div data-draggable-cont="rows">
	<div data-draggable-id="1" data-draggable-name="first">First</div>
	<div data-draggable-id="2" data-draggable-name="second">Second</div>
	<div data-draggable-id="3" data-draggable-name="third">Third</div>
</div>

Only the direct children of the container take part. The container is scanned at DOMContentLoaded and, if the legacy FrontEnd module is present, on every onHtmlChange — so rows loaded later via ajax become draggable too. Already processed elements are flagged with data-draggable-set and skipped. Call checkDraggables() yourself if you inject markup without FrontEnd.

Note that the container gets position: relative and the dragged element position: absolute, so the container must be able to host an absolutely positioned child.

Attributes

On the container:

  • data-draggable-cont: marks the container; its value is the name used by getDraggableList.
  • data-draggable-callback: the body of a JavaScript function run after a successful drop, with this bound to the dragged element and two arguments, element and target (see below).

On the children:

  • data-draggable-id: an arbitrary identifier handed to the callback. Typically the database id of the row.
  • data-draggable-name: an arbitrary name, returned by getDraggableList in place of the index.
  • data-draggable-index: the ordering index. Assigned automatically if absent — elements that already carry one keep it, and the ones without are numbered after the highest already in use.
  • data-draggable-ignore: excludes the element from the ordering entirely; it stays where it is and is never a drop target. The attribute may be bare (data-draggable-ignore) or given any value.
  • data-draggable-parent: restricts the element to reordering within its own group. Elements with different data-draggable-parent values are never dragged past one another, so a single container can hold several independent orderings (children of different parents in a tree, for example). Elements without the attribute all belong to the same default group.
  • data-draggable-grip: put this on a descendant of a child to make that descendant the only handle the drag can start from. Without it the whole child is draggable.

The callback

<div data-draggable-cont="rows" data-draggable-callback="
	ajax('reorder', {}, {id: element.id, after: target.id});
">

element describes the dragged element and target the one it was dropped onto, each as {id, idx}id being data-draggable-id and idx the data-draggable-index as it was before the drop. Inside the body, this is the dragged DOM element.

The callback does not run when the element is dropped back where it started; that is treated as a plain click instead, and the click is forwarded to whatever was originally under the cursor — so rows that are both clickable and draggable keep working.

Reading the order back

getDraggableList('rows'); // ['second', 'first', 'third']

Returns the current order of the container as an array of data-draggable-name values, falling back to data-draggable-index for elements without a name and to null for those with neither (including the ignored ones, which keep their slot in the array). Returns an empty array if no container by that name exists.