Touch-Friendly JavaScript Drag and Drop Sortable Library – JSort
How To Use It:
1. Install the package via NPM.
npm install @rbuljan/jsort
2. Create your HTML structure. You can use any parent tag, such as a <ul> or a <div>.
<ul id="my-list">
<li>Item 1</li>
<li>Item 2</li>
<li>
<!-- Optional drag handle -->
<span class="handle">::</span>
Item 3
</li>
<li>Item 4</li>
</ul> 3. Initialize the library in your JavaScript file.
import JSort from '@rbuljan/jsort';
// Initialize the sortable list
// The first argument is the container element
// The second argument is the configuration object
const sortable = new JSort(document.getElementById("my-list"), {
duration: 300,
selectorHandler: '.handle', // Optional: restricts drag to the handle
onDrop: (data) => {
// Log the data to see the new index
console.log('Item dropped:', data);
}
}); 4. Available configuration options. You can pass these options to the second argument of the JSort constructor:
group (string): Links multiple sortable containers. Items can be dragged between containers with matching group names. Default is empty string (no linking).swap (boolean): Changes drop behavior to swap positions instead of reordering. Useful for ranked lists or team rosters. Default is false.duration (number): Animation duration in milliseconds for the sort animation. Default is 420.easing (string): CSS easing function for animations. Default is “cubic-bezier(0.6, 0, 0.6, 1)”.scale (number): Scale factor for the ghost element that follows the pointer. Default is 1.1.opacity (number): Opacity value for the ghost element, ranging from 0 to 1. Default is 0.8.grabTimeout (number): Delay in milliseconds before grab activates on touch devices. This allows scroll-intent detection. Default is 140. Has no effect on mouse events.parentDrop (boolean): Allows dropping items directly onto the parent container instead of between items. Default is true.moveThreshold (number): Distance in pixels the pointer must move before drag activates. Prevents accidental drags when clicking links or buttons. Default is 0.scrollThreshold (number): Distance in pixels to consider touch movement as scrolling instead of dragging. Default is 8.edgeThreshold (number): Distance in pixels from scrollable container edge to trigger auto-scroll. Default is 50.scrollSpeed (number): Scroll velocity in pixels per animation frame during edge auto-scroll. Default is 10.zIndex (number): Z-index value for the ghost element. Default is 2147483647 (maximum 32-bit signed integer).selectorParent (string): CSS selector for parent sortable containers. Default is “.jsort”.selectorItems (string): CSS selector for sortable items within the parent. Uses immediate children by default. Default is “*”.selectorItemsIgnore (string): CSS selector for ignored children that should not be sortable. Default is “.jsort-ignore”.selectorHandler (string): CSS selector for drag handle elements within items. Default is “.jsort-handler”.selectorIgnoreTarget (string): CSS selector for item descendants that should prevent grab when clicked. Default is empty string.selectorIgnoreFields (string): CSS selector for action elements that should prevent grab. Default targets inputs, buttons, links, and other interactive elements.classGhost (string): Class name applied to the ghost element. Default is “is-jsort-ghost”.classActive (string): Class name applied on pointer down. Default is “is-jsort-active”.classTouch (string): Class name applied only on touch events. Default is “is-jsort-touch”.classGrab (string): Class name applied to the grabbed item. Default is “is-jsort-grab”.classTarget (string): Class name applied to the hovered target element. Default is “is-jsort-target”.classAnimated (string): Class name applied to all animated elements during drop. Default is “is-jsort-animated”.classAnimatedDrop (string): Class name applied to the grabbed item during drop animation. Default is “is-jsort-animated-drop”.classInvalid (string): Class name applied to ghost element over invalid drop zones. Default is “is-jsort-invalid”.onBeforeGrab (function): Callback invoked before grab activates. Return false to cancel the grab operation.onGrab (function): Callback invoked when item grab activates.onMove (function): Callback invoked during pointer movement while dragging.onBeforeDrop (function): Callback invoked before drop completes. Return false to cancel the drop operation.onDrop (function): Callback invoked after successful drop.onAnimationEnd (function): Callback invoked when drop animation completes.const sortable: new JSort(document.getElementById("my-list"), {
elGrabParent: el,
group: "",
swap: false,
duration: 420,
easing: "cubic-bezier(0.6, 0, 0.6, 1)",
scale: 1.1,
opacity: 0.8,
grabTimeout: 140,
parentDrop: true,
moveThreshold: 0,
scrollThreshold: 8,
edgeThreshold: 50,
scrollSpeed: 10,
zIndex: 2147483647 // 0x7FFFFFFF,
selectorParent: ".jsort",
selectorItems: "*",
selectorItemsIgnore: ".jsort-ignore",
selectorHandler: ".jsort-handler",
selectorIgnoreTarget: "",
selectorIgnoreFields: `:is(input, select, textarea, button, label, [>
5. Or define them directly in HTML using the data-jsort attribute as follows:
The data-jsort format uses semicolons to separate options. Use colons to separate option names from values. The library automatically parses numbers and booleans from string values.
<div id="playerRoster" data-jsort="
group: team-a;
selectorItems: .player;
selectorHandler: .handle;
swap: true;
duration: 300;
easing: ease-out;
zIndex: 999;
parentDrop: false;
">
<div class="player">
<div class="handle">⋮⋮</div>
<span>Player One</span>
</div>
<div class="player">
<div class="handle">⋮⋮</div>
<span>Player Two</span>
</div>
</div> new JSort(document.querySelector("#playerRoster")); 6. JSort provides methods to control the sortable instance programmatically:
// Re-initialize with new options
sortableInstance.init({
duration: 500,
swap: true
});
// Clean up and remove event listeners
sortableInstance.destroy();
// Programmatically insert an item
const newItem = document.createElement('div');
newItem.textContent = 'New Task';
sortableInstance.insert(newItem, targetElement);
// Sort items with animation (Beta feature)
sortableInstance.sort((a, b) => {
// Sort alphabetically by text content
return a.textContent.localeCompare(b.textContent);
}); 7. Event handlers.
new JSort(document.querySelector("#myList"), {
// Called before grab activates (return false to cancel)
onBeforeGrab(data) {
console.log('Before grab:', data.elGrab, data.indexGrab);
console.log('Event:', data.event);
// Access instance properties via 'this'
console.log('Parent element:', this.elGrabParent);
},
// Called when grab activates
onGrab(data) {
console.log('Grabbed element:', data.elGrab);
console.log('From parent:', data.elGrabParent);
console.log('Original index:', data.indexGrab);
},
// Called during pointer movement
onMove(data) {
console.log('Moving over:', data.elTarget);
console.log('Valid target:', data.isValidTarget);
console.log('Ghost element:', data.elGhost);
},
// Called before drop completes (return false to cancel)
onBeforeDrop(data) {
console.log('Attempting drop at index:', data.indexDrop);
console.log('Is valid:', data.isValidTarget);
console.log('Same parent:', data.isSameParent);
},
// Called after successful drop
onDrop(data) {
console.log('Moved from', data.indexGrab, 'to', data.indexDrop);
console.log('Dropped element:', data.elDrop);
console.log('New parent:', data.elDropParent);
console.log('Affected elements:', this.affectedElements);
},
// Called when animation completes
onAnimationEnd() {
console.log('Animation finished');
// All affected elements have reached final positions
}
}); 8. Access instance properties to read the current drag state or affected elements.
const sortable = new JSort(document.querySelector("#list"));
// During drag operations:
console.log(sortable.indexGrab); // Index of grabbed item (-1 when not dragging)
console.log(sortable.indexDrop); // Target drop index (-1 when not dragging)
console.log(sortable.elGrab); // Grabbed element reference
console.log(sortable.elGrabParent); // Grabbed item's parent container
console.log(sortable.elGhost); // Ghost element that follows pointer
console.log(sortable.elTarget); // Currently hovered target
console.log(sortable.elDrop); // Final drop target element
console.log(sortable.elDropParent); // Drop target's parent container
console.log(sortable.affectedElements); // Array of elements moved by the drop 9. JSort works without CSS but benefits from minimal styling for visual feedback. Add these styles to improve the user experience.
/* Highlight active item on touch devices */.is-jsort-active.is-jsort-touch {
outline: 2px solid currentColor;
}
/* Hide the original grabbed element */.is-jsort-grab {
opacity: 0;
}
/* Highlight valid drop targets */.is-jsort-target {
z-index: 1;
outline: 2px dashed currentColor;
}
/* Show invalid drop zones */.is-jsort-invalid {
outline: 2px solid red;
} Alternatives
Q: How do I prevent users from dragging specific items?
A: Add the selectorItemsIgnore class to items you want to exclude. Configure this through the options object or use a custom class name. You can also use the onBeforeGrab callback to conditionally prevent grabs based on element properties or state.
Q: Can I use JSort with dynamically added items?
A: Yes. JSort uses event delegation on the parent container. Items added to the DOM after initialization work automatically without re-initialization.
Q: How do I persist the new order after users reorder items?
A: Use the onDrop callback to capture the new order. Query all items and extract their IDs or data attributes, then send this data to your server.
Q: Why isn’t my list scrolling when I drag to the edge?
A: The parent container must have a defined height and overflow: auto or overflow: scroll in your CSS. JSort detects the scrollable parent automatically.
The post Touch-Friendly JavaScript Drag and Drop Sortable Library – JSort appeared first on CSS Script.
50 Years Ago A number of area residents attended a slide presentation by the Northampton…
Jameson Fournier,11, a member of the Western Mass 4-H Ox teamsters, leads his two steers,…
President Donald Trump addressed the nation in his State of the Union Tuesday night —…
HADLEY — Significant reductions to teaching staff and education support professionals at the Hadley Elementary…
The post Photo: Snowblower fix appeared first on Daily Hampshire Gazette.
SOUTH HADLEY — The town has slid out of its pickleball court pickle. Over the…
This website uses cookies.