Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dragselect

Package Overview
Dependencies
Maintainers
1
Versions
97
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dragselect

easy javascript drag select & drop functionality for your projects

  • 2.5.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
10K
increased by10.52%
Maintainers
1
Weekly downloads
 
Created
Source
    ____                   _____      __          __ 
   / __ \_________ _____ _/ ___/___  / /__  _____/ /_
  / / / / ___/ __ `/ __ `/\__ \/ _ \/ / _ \/ ___/ __/
 / /_/ / /  / /_/ / /_/ /___/ /  __/ /  __/ /__/ /_  
/_____/_/   \__,_/\__, //____/\___/_/\___/\___/\__/  
                 /____/                              

GitHub | NPM | Project-Page

Build Status gzip size npm downloads count No Dependency Contributors Welcome Sponsors Welcome

DragSelect GitHub package.json version

easily add a selection algorithm to your application/website.

TOC

Project Page: Demo & Info

https://dragselect.com/

Key-Features

  • No dependencies No Dependency
  • Hyper customizable
  • Replicates operating system drag-selection in the browser
  • Accessibility (a11y)
  • Add drag selection
  • Use modifier keys to make multiple independent selections
  • Select, Drag and Drop also via keyboard
  • Choose which elements can be selected
  • Supports all major browsers
  • Selected elements can be dragged and dropped
  • Lightweight, only gzip size
  • Popular: npm downloads count on npm
  • DragSelect was written with Performance in mind (can easily select >15.000 Elements)
  • Supports SVG
  • Supports mobile (touch interaction)
  • Free & open source under MIT License
  • Ease of use

demo-gif

Why?

Because apparently there was nothing that does not require jquery out there.
This is better than https://jqueryui.com/selectable/ or https://jqueryui.com/draggable/ and has no dependencies. We use it currently in a professional rich interface application where we have a file management system. The user can select files to organize them and change their metadata, with this plugin our users are able to select multiple files and perform batch/bulk-operations (applying changes to multiple files at once). We also started using it in production for a huge, graphical cloud hosting manager with a lot of active users. Users can select multiple servers, storages, etc. on an artboard to perform multi-operations, re-organize them, move them on the UI or batch-delete. We’re running it since January 18' it’s super helpful and very stable, let’s keep it that way. I can easily think of dozens other use-cases. I’m really keen to know how you use it in your projects, please let me know.

Supporters

Please donate to support the countless hours of hard work & support. Especially if your company makes money, then there is no excuse. Thank you :)

If you're too poor or broke you can still support us with your time instead by contributing to the code!

Thanks to:

BrowserStackYou?
BrowserStack is a testing service which helps testing the tool on various browsers. They support this open source projects by providing us with a free account!Thank and support us by making a Direct Donation to DragSelect (via Bitcoin: 1LdweSpjgSeJC8XxX3swrohBMBLUzg6cmC). Or sponsor via GitHub Sponsors or Get in touch.
Via BTC:1LdweSpjgSeJC8XxX3swrohBMBLUzg6cmC (direct)
Via GitHub:GitHub Sponsors (monthly & one-time)
Other:get in touch

All donations are distributed with all project contributors proportionally to their involvement. We are grateful for any amount:
We have more than npm downloads count, imagine how much we'd have if everyone would have had donated only 1$ 🤩 (unfortunately this did not happen)

If you donate, we can display your logo here if you want, which will give you fame, fortune and help you recruit great talent and boosting your SEO.

Installation

NPM

npm install --save dragselect

Yarn

yarn add dragselect

Global

Just download the file (minified) and add it to your document:

<script src="https://dragselect.com/v2/ds.min.js"></script>

Note: if you are using <script type=module you can use the DragSelect.es6m.js or ds.es6m.min.js files as they include export default DragSelect

We don't recommend the direct linking for production set-up since you'll not benefit from versioning. Please use npm if you can.

Bower (deprecated)

bower install --save dragselect

Note: the Bower project is deprecated. Please use npm instead. If you have to use bower, you'll have to build the project after installing it via npm run rollup

That's it, you're ready to rock!

Of course you can also just include the code within your code and bundle it to save a request.

DragSelect supports module.exports, AMD Modules with define, es6 modules with .es6m versions and has a fallback to global namespace for maximum out of the box support.

Usage

Now in your JavaScript you can simply pass elements to the function like so:

Simple

The simplest possible usage.
Choose which elements can be selected:

new DragSelect({
  selectables: document.getElementsByClassName('selectable-nodes')
});

See the Pen prpwYG on CodePen.

Within a scroll-able Area

Here the selection is constrained. You can only use the selection/drag inside of the area container:

new DragSelect({
  selectables: document.getElementsByClassName('selectable-nodes'),
  area: document.getElementById('area')
});

See the Pen DragSelect with Scrollable AREA on CodePen.

With DropZones

Example of DropZones. DropZones are areas where you can drop the selected elements into.

const ds = new DragSelect({
  selectables: document.querySelectorAll('.selectable-nodes'),
  area: document.querySelector('#area'),
  dropZones: [
    { element: document.querySelector('#zone-1'), id: 'zone-1', droppables: document.querySelectorAll('.selectable-nodes') },
    { element: document.querySelector('#zone-2'), id: 'zone-2', droppables: document.querySelectorAll('#item-2,#item-4') },
  ],
  dropInsideThreshold: 1, // 1 = has to be 100% inside the dropzone, 0.5 = 50% inside, 0 = just touching is fine
});

ds.subscribe('callback', ({
  dropTarget: {
      id: "zone-1",
      element: <node />,
      droppables: [ <node />, … ],
      itemsDropped: [ <node />, … ], // elements that were selected on drop
      itemsInside: [ <node />, … ] // elements that are inside the bounds of the dropzone
  }) => {
  if(dropTarget?.itemsDropped?.length) {
    // do something
    console.log('Dropped', dropTarget.itemsDropped, 'into', dropTarget.id);
  }
})

This will also add some respective classes

See the Pen DragSelect with DropZones on CodePen.

Extended

All options are optional. You could also just initiate the Dragselect by new DragSelect({}); without any option.
Find all possible properties and methods in the docs

// if you add the function to a variable like this, you have access to all its functions
const ds = new DragSelect({
  // all settings are optional and can be added later
});

// this is how you add/update settings after initialization
ds.setSettings({
  // node/nodes that can be selected. By default will never add elements twice:
  selectables: document.querySelectorAll('.selectable-nodes'),
  // area in which you can drag. If not provided it will be the whole document & body/documentElement.
  area: document.getElementById('area'),
  // and many more, see "properties" section in the docs
})

// fired once the user releases the mouse. (items) = selected nodes:
ds.subscribe('callback', ({ items, event }) => {})
// returns all currently selected nodes:
ds.getSelection();
// Teardown/stop the whole functionality:
ds.stop();
// Reset the functionality after a teardown:
ds.start();

// and many more, see "methods" section in documentation

You can also use the "shift", "ctrl" or "command" key to make multiple independent selections.

Mobile/Touch usage

Keep in mind that using DragSelect on a mobile/touch device will also turn off the default scroll behavior (on click + drag interaction). In 99% of the use-cases, this is what you want. If DragSelect is only one part of a website, and you still want to be able to scroll the page on mobile, you can use an area property. This way the default scroll behavior remains intact for the rest of the page.

Accessibility (a11y)

DragSelect is accessible by default:

TLDR;
=> Your selectables should be buttons: <button type="button"></button>.
=> ArrowKeys are used for keyboard dragging.

Obviously, keyboard users won’t get the full visual experience but it works similarly to the OS default behavior.

  1. Selection: You can select items using the default select keys (usually space or enter) and also multi-select when using a modifier key at the same time. There is one little thing you have to do tho’: the selectables have to be pressable (clickable)! To achieve this, they should be of type <button type="button"></button>.

  2. Drag: You can drag elements using the keyboard arrow keys, this will also scroll the area by default. You can press Shift in combination with an arrow i.e. Shift+ArrowRight to move the element 4x faster to the right and also not scroll the area

See the Pen DragSelect on CodePen.

Use your own Drag And Drop

Using another plugin/tool (3rd party)

DragSelect comes with a build-in dragNdrop. Before, .break was used for this. But with v2, using your own is now very simple: listen to any DragSelect event to .stop it. Then, re-.start it after your custom dragNdrop was performed. Check for isDragging, which indicates when the users drags (moving the element) and isDraggingKeyboard for the keyboard drag events. I.e. use predragstart.

Example
const ds = new DragSelect({ 
  keyboardDragSpeed: 0, 
  // keyboardDrag: false, // if your library can not handle keyboard dragging
  /* …other settings… */
})
const myCustomDrag = new MyCustomDrag({/* …your settings… */})

ds.subscribe('predragstart', ({ isDragging, isDraggingKeyboard }) =>
  isDragging && ds.stop(false, false))
myCustomDrag.subscribe('finished', () => ds.start())

Disabling then re-enabling directly can also work (i.e. when your library has no callback):

ds.subscribe('predragstart', ({ isDragging, isDraggingKeyboard }) => {
   if(isDragging) {
     ds.stop(false, false)
     setTimeout(ds.start)
   }
})

Note: it is important to debounce (i.e. with setTimeout(ds.start)) the start function if it's called within a single subscriber so that all the scheduled callbacks finish triggering before we start again.

Writing a fully custom solution

In case you want to build something completely custom on top of DragSelect, we got you covered! You can use .break for this. You heard right, break is back baby :)

This utility to override DragSelects internal functionality allows you to write it all yourself: You can write your own drag and drop but you can also write your own selection:

Example

/!\ only use break when you know what you're doing. Support is limited /!\

ds.subscribe('predragmove', ({ isDragging, isDraggingKeyboard }) => {
  if(isDragging || isDraggingKeyboard) {
    ds.break()
    /* your custom logic for drag handling here. */
  } else {
    ds.break()
    /* your custom logic for selection handling here. */
  }
}

Constructor Properties (Settings)

DragSelect is hyper customizable. Note, all properties are optional. See the docs for more info.

Moreover any setting can also be updated or added after the initialization, see post-initialization setting updates.

Here is the full list:

propertytypeusagedefault
areasingle DOM element (node)The square in which you are able to selectdocument
selectablesDOM elements [nodes]The elements that can be selected[]
autoScrollSpeednumberThe speed in which the area scrolls while selecting (if available). The unit is arbitrary (interval aims for 30fps). Set to 0.0001 to disable auto-scrolling.5
overflowTolerance{ x:number, y:number }Tolerance for autoScroll (how close one has to be near an edges for autoScroll to start){x:25,y:25}
zoomnumberZoom scale factor (in case of using CSS style transform: scale() which messes with real positions). Unit scale zoom.1
customStylesbooleanIf true, no styles will be automatically applied to the selector element (except position: absolute).false
multiSelectModebooleanAdd newly selected elements to the selection instead of replacing them.false
multiSelectTogglingbooleanWhether or not to toggle already active elements while multi-selecting.true (MacOS selection behavior)
multiSelectKeysarrayKeys that allows switching to the multi-select mode (see the multiSelectMode option). Any key value is possible (see MDN docs). Note that the best support is given for Control, Shift and Meta. Provide an empty array [] if you want to turn off the functionality.['Control', 'Shift', 'Meta']
selectorsingle DOM element (node)The square that will be used to draw the selection.Auto-created HTML Element
selectionThresholdnumberHow much % of the element has to be selected to be considered selected (0 = just touching, 1 = fully inside the selection)0
draggabilitybooleanWhen a user is dragging on an already selected element, the selection is dragged.true
immediateDragbooleanWhether a selectable element is draggable before being selected or needs to be selected firsttrue
keyboardDragbooleanWhether or not the user can drag with the keyboard (Accessibility).true
dragKeys{ up:string[], down:string[], left:string[], righ:string[] }The keys available to drag element using the keyboard. Any key value is possible (see MDN docs).{ up:['ArrowUp'], down: ['ArrowDown'], left: ['ArrowLeft'], righ: ['ArrowRight'] }
keyboardDragSpeednumberThe speed at which elements are dragged using the keyboard. In pixels per keyDown.10
useTransformbooleanWhether to use the more performant hardware accelerated css transforms when dragging instead of the top/left positions.true
refreshMemoryRatenumberRefresh rate on memoization, higher numbers mean better performance but more lag if elements are moving, lower numbers mean less lag but worse performance. If none of your DOMNodes are moving, you can set it to a very high number to increase performance. Value in milliseconds.80
dropZones[{ id: 'string', element: single DOM element (node), droppables: DOM elements [nodes] }]zones with association of droppable items that can be dropped into them[]
dropInsideThresholdnumberHow much % of the item has to be inside the dropzone to be considered inside (0 = barely touching, 1 = completely inside)1
dropTargetThresholdnumberHow much % of the zone does the pointer has to be in to be considered a target (0 = anywhere in the zone, max: 0.5 = has to point at the center of the zone)0
usePointerEventsbooleanWhether to use Pointer Events to replace traditional Mouse or Touch Events. Useful for tools like Google Blockly.false
selectedClassstringThe class name assigned to the selected items.see classes
hoverClassstringThe class name assigned to the mouse hovered items.see classes
selectorClassstringThe class name assigned to the square selector helper.see classes
selectableClassstringThe class name assigned to the elements that can be selected.see classes
selectorClassstringThe class assigned to the square selector helperds-selector
selectorAreaClassstringThe class assigned to the square in which the selector resides. By default it's invisibleds-selector-area
droppedTargetClassstringOn an item corresponding the target dropzone. This is also the prefix for ds-dropped-target-${zone.id}.ds-dropped-target & ds-dropped-target-${zone.id}
droppedInsideClassstringOn an item that is within its dropzone bounds after a drop. This is also the prefix for ds-dropped-inside-${zone.id}ds-dropped-inside & ds-dropped-inside-${zone.id}
droppableClassstringOn element that can be dropped into at least one container. This is also the prefix for ds-droppable-${zone.id}ds-droppable & ds-droppable-${zone.id}
dropZoneClassstringOn each dropZoneds-dropzone
dropZoneReadyClassstringOn corresponding dropZone when element is draggedds-dropzone-ready
dropZoneTargetClassstringOn dropZone that has elements from any successful target dropds-dropzone-target
dropZoneInsideClassstringOn dropZone that has elements inside after any dropds-dropzone-inside

Post-Initialization Setting-Updates

Any setting can be updated/added after initialization by using the setSettings method. Here is an example updating the area and the selectables:

const ds = new DragSelect({})
ds.setSettings({
  selectables: document.getElementsByClassName('selectable-nodes'),
  area: document.getElementById('area')
})

Event Callbacks

Event Callbacks are used like this:

ds.subscribe('<event_name>', (callback_object) => {})

Events

event_namecallback_objectdescription
callback{ items, event, isDragging, … }Fired after the selection (i.e. on mouse-up).
dragstart{ items, event, isDragging, isDraggingKeyboard, … }Fired when the selection starts (i.e. on mouse-down).
dragmove{ items, event, isDragging, isDraggingKeyboard, … }Fired when the mouse moves while dragging (i.e. on mouse-move).
autoscroll{ items, isDragging, scroll_directions, scroll_multiplier, … }Fired when the area is auto-scrolled (i.e. cursor on a corner of the area).
elementselect{ items, item, … }Fired when an element is added to the selection.
elementunselect{ items, item, … }Fired when an element is removed from the selection.

Note: all your callbacks subscribers will run happen after the internal code ran. If you want to run something before everything else, use the pre prefix. I.e. predragstart is an event that runs before any internal start logic.

Callback Object Keys

callback_object_keystypedescription
eventMouseEvent|TouchEvent|KeyboardEventThe native HTML Event, depending on the situational context
itemsArray.<HTMLElement|SVGElement|*>Current selected elements
isDraggingbooleanIf true, the user is dragging the selected elements, if false the user is drawing a selection
isDraggingKeyboardbooleanIf true, the user is dragging the selected elements with the keyboard
scroll_directionsArray.<'top'|'bottom'|'left'|'right'|undefined>The direction in which the event is happening (i.e. scroll direction)
scroll_multipliernumberSpeed
itemHTMLElement|SVGElement|*The single element currently being interacted with if any
dropTarget{ id: 'id', element: node, droppables: [node], itemsDropped: [node], itemsInside: [node] }dropZone in which the droppable elements were dropped into. itemsDropped: all items that were dropped on the target. itemsInside: all items that are within the bounds of the zone

Note: all object keys are optional and might not be available, depending on the event type. So make sure to check for availability first

Methods

When the function is saved into a variable var foo = new DragSelect() you have access to all its inner functions.
Also check the docs for more info.

methodpropertiesusage
stop/Will teardown/stop the whole functionality
start/Reset the functionality after a teardown
break/Utility to override DragSelect internal functionality. Breaks out of current flow. Read docs for more info.
setSettingssettings:SettingsUpdate any setting dynamically, see Settings
getSelection/Returns all currently selected nodes
addSelectionelements:DOM Elements, triggerCallback:boolean, dontAddToSelectables:booleanadds one or multiple elements to the selection. If boolean is set to true: callback will be called afterwards. Adds them to the selectables if they're not yet in the set (can be turned off by setting the last boolean to true)
removeSelectionelements:DOM Elements, triggerCallback:boolean, removeFromSelectables:booleanremoves one or multiple elements to the selection. If boolean is set to true: callback will be called afterwards. If last boolean is set to true, it also removes them from the possible selectable nodes if they were.
toggleSelectionelements:DOM Elements, triggerCallback:boolean, alsoSelectables:booleantoggles one or multiple elements to the selection. If element is not in selection it will be added, if it is already selected, it will be removed. If boolean is set to true: callback will be called afterward. If last boolean is set to true, it also removes selected elements from possible selectable nodes & doesn’t add them to selectables if they are not (can be turned off by setting the last boolean to true).
setSelectionelements:DOM Elements, triggerCallback:boolean, dontAddToSelectables:booleansets the selection to one or multiple elements. If boolean is set to true: callback will be called afterwards. Adds them to the selectables if they're not yet in the set (can be turned off by setting the last boolean to true)
clearSelectionDOM elements [nodes], Boolean (callback)remove all elements from the selection. If boolean is set to true: callback will be called afterwards.
clearSelectiontriggerCallback:booleanUnselect / Deselect all current selected Nodes
addSelectableselements:DOM Elements, addToSelection:booleanAdds elements that can be selected. Don’t worry, nodes are never added twice. If boolean is set to true: elements will also be added to current selection.
removeSelectableselements:DOM Elements, removeFromSelection:booleanRemove elements from the set of elements that can be selected. If boolean is set to true: elements will also be removed from current selection.
getSelectables/Returns array with all nodes that can be selected.
getInitialCursorPosition/Returns the registered x, y coordinates the cursor had when first clicked
getCurrentCursorPosition/Returns current x, y coordinates of the cursor
getPreviousCursorPosition/Returns last registered x, y coordinates of the cursor (after last callback)
getInitialCursorPositionArea/Returns the registered x, y coordinates relative to the area the cursor had when first clicked
getCurrentCursorPositionArea/Returns current x, y coordinates of the cursor relative to the area
getPreviousCursorPositionArea/Returns last registered x, y coordinates of the cursor relative to the area (after last callback)
isMultiSelect`event:KeyboardEventMouseEvent
isDragging/Whether the user is currently drag n dropping elements (instead of selection)
getZoneByCoordinatesOptional {x: number, y: number}Returns the first drop target under the current mouse position, or, if provided at coordinates x/y
getItemsDroppedByZoneIdzoneId: stringReturns the items dropped into a specific zone (by zone.id)
getItemsInsideByZoneIdzoneId: string,addClasses: booleanReturns the items that are visually inside a specific zone (by zone.id)

Classes

nametrigger
.ds-selectedOn items that are selected
.ds-hoverOn items that are currently hovered
.ds-selectorOn the selector element
.ds-selector-areaThe overlay where the selector resides in
.ds-selectableOn items that can be selected
.ds-droppableon item that can be dropped into at least one zone
.ds-droppable-${id}on item that can be dropped into a zone with specific identifier, ${id} will be replaced by the corresponding zone.id
.ds-dropped-targeton an item corresponding the target dropzone
.ds-dropped-target-${id}on an item corresponding the target dropzone with specific identifier, ${id} will be replaced by the corresponding zone.id
.ds-dropped-insideon an item that is within its dropzone bounds after a drop
.ds-dropped-inside-${id}on an item that is within its dropzone bounds after a drop with specific identifier, ${id} will be replaced by the corresponding zone.id
.ds-dropzoneon each dropZone
.ds-dropzone-readyon corresponding dropZone when corresponding item is being dragged
.ds-dropzone-targeton dropZone when it was target of a successful drop
.ds-dropzone-insideon dropZone that has elements inside after any drop

note: you can change the class names setting the respective property on the constructor, see the docs properties section.

Have Fun!

Creating and maintaining useful tools is a lot of work. So don’t forget to give this repository a star if you find it useful. Star this repo, tell all your friends and start contributing and/or donating 1$ to keep it running. Thank you :)

Typewriter Gif




documentation

Keywords

FAQs

Package last updated on 08 Nov 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc