{"id":13598225,"url":"https://github.com/anseki/plain-draggable","last_synced_at":"2025-04-08T10:28:59.464Z","repository":{"id":40284485,"uuid":"83285134","full_name":"anseki/plain-draggable","owner":"anseki","description":"The simple and high performance library to allow HTML/SVG element to be dragged.","archived":false,"fork":false,"pushed_at":"2023-10-28T16:14:09.000Z","size":2044,"stargazers_count":746,"open_issues_count":0,"forks_count":92,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-04-14T09:38:28.153Z","etag":null,"topics":["dom","drag","draggable","grid","html","performance","requestanimationframe","snaps","svg","ui"],"latest_commit_sha":null,"homepage":"https://anseki.github.io/plain-draggable/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anseki.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-02-27T08:11:45.000Z","updated_at":"2024-04-12T01:58:24.000Z","dependencies_parsed_at":"2024-01-16T12:01:13.565Z","dependency_job_id":null,"html_url":"https://github.com/anseki/plain-draggable","commit_stats":{"total_commits":301,"total_committers":1,"mean_commits":301.0,"dds":0.0,"last_synced_commit":"362d864a9d9b6366f829d12476d419e54f7b8286"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fplain-draggable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fplain-draggable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fplain-draggable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fplain-draggable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anseki","download_url":"https://codeload.github.com/anseki/plain-draggable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247823152,"owners_count":21002017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","drag","draggable","grid","html","performance","requestanimationframe","snaps","svg","ui"],"created_at":"2024-08-01T17:00:50.490Z","updated_at":"2025-04-08T10:28:59.445Z","avatar_url":"https://github.com/anseki.png","language":"JavaScript","readme":"# PlainDraggable\n\n[![npm](https://img.shields.io/npm/v/plain-draggable.svg)](https://www.npmjs.com/package/plain-draggable) [![GitHub issues](https://img.shields.io/github/issues/anseki/plain-draggable.svg)](https://github.com/anseki/plain-draggable/issues) [![dependencies](https://img.shields.io/badge/dependencies-No%20dependency-brightgreen.svg)](package.json) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nThe simple and high performance library to allow HTML/SVG element to be dragged.\n\n**\u003ca href=\"https://anseki.github.io/plain-draggable/\"\u003eDocument and Examples https://anseki.github.io/plain-draggable/\u003c/a\u003e**\n\n[![ss-01](ss-01.png)](https://anseki.github.io/plain-draggable/)\n\n**Features:**\n\n- Accept HTML/SVG element as an element that comes to be draggable.\n- Support both mouse and touch interfaces.\n- Restrict the draggable area.\n- Snap the draggable element to other elements, points, lines, consecutive points (i.e. grid) or something.\n- Scroll an element or window automatically when the draggable element was moved to edge of the area.\n- Use `requestAnimationFrame` API, `translate` and `will-change` CSS if possible, for high performance.  \n![ss-02](ss-02.png)\n- Provide only basic methods, easy and simple usage.\n- No dependency.\n- Single file.\n- Support modern browsers.\n\n## Usage\n\nLoad PlainDraggable into your web page. (Or, [install it](#development) for importing.)\n\n```html\n\u003cscript src=\"plain-draggable.min.js\"\u003e\u003c/script\u003e\n```\n\nThis is simplest case:\n\n```js\ndraggable = new PlainDraggable(element);\n```\n\nNow, the `element` can be moved by mouse-dragging (or touch interfaces) operation.  \nThe `element` might be a `\u003cdiv\u003e`, `\u003cspan\u003e`, `\u003ccircle\u003e` or something.\n\nBy default, the moving is restricted to inside of the `element`'s parent element.\n\n```html\n\u003cdiv style=\"background-color: gray;\"\u003e\n  \u003cdiv id=\"draggable\"\u003eDrag This\u003c/div\u003e\n\u003c/div\u003e\n```\n\nThe `\u003cdiv id=\"draggable\"\u003e` can be moved within the gray box. (See [`containment`](#options-containment) option.)\n\nYou can specify a \"Grid\" that the draggable element snaps to.\n\n```js\ndraggable.snap = {step: 30};\n```\n\nFor options, methods and more details, refer to the following.\n\n## Constructor\n\n```js\ndraggable = new PlainDraggable(element[, options])\n```\n\nThe `element` argument is a HTML or SVG element that will be a draggable element. Any element that has a bounding-box is accepted. Other elements such as `\u003cg\u003e` can not be specified.\n\nThe `options` argument is an Object that can have properties as [options](#options) (and [`leftTop`](#lefttop-option) option for constructor). You can also change the options by [`setOptions`](#setoptions) method or [properties](#properties) of the PlainDraggable instance.\n\nWhen you will do something about HTML document regardless of the PlainDraggable, you typically do that after the HTML document is ready (i.e. the HTML document has been loaded and parsed by web browser).  \nFor example:\n\n```js\n// Wait for HTML document to get ready\nwindow.addEventListener('load', function() { // NOT `DOMContentLoaded`\n  // Do something about HTML document\n  var draggable = new PlainDraggable(document.getElementById('target'));\n});\n```\n\nIf you don't wait for HTML document to be ready, you might not be able to get a target element yet, or problems with incomplete layout may occur. Also, you should do so asynchronous like the above for the performance because synchronous code blocks parsing HTML.\n\nBefore HTML document is ready, the `translate` CSS function might not be initialized yet. If so, PlainDraggable tries to initialize a draggable element with the `left` and `top` CSS properties regardless of [`leftTop`](#lefttop-option) option.  \nThis may be performance degradation.\n\n### `leftTop` option\n\nBy default, PlainDraggable tries to move the draggable element by using the `translate` CSS function.  \nIf `true` is specified for `leftTop` option of the constructor, it uses the `left` and `top` CSS properties instead, for HTML element. You may use this to make it synchronize with the layout of something.\n\nNote that the `left` and `top` CSS properties may be performance degradation.  \nAlso, this is an option for constructor, that is, you can not change this after.\n\n## Methods\n\n### `setOptions`\n\n```js\nself = draggable.setOptions(options)\n```\n\nSet one or more options.  \nThe `options` argument is an Object that can have properties as [options](#options).\n\n### `position`\n\n```js\nself = draggable.position()\n```\n\nRe-calculate the position of the draggable element, [`containment`](#options-containment) option, [`snap`](#options-snap) option and [`autoScroll`](#options-autoscroll) option, with current layout of a page.  \nThose are re-calculated as needed automatically when a window is resized, or a window or something is scrolled.  \nYou should call `position` method if you changed the layout without resizing the window or scrolling something.\n\n### `remove`\n\n```js\ndraggable.remove()\n```\n\nRemove the current PlainDraggable instance.  \nNever use the removed instance.\n\n## Options\n\n### \u003ca name=\"options-containment\"\u003e\u003c/a\u003e`containment`\n\n*Type:* HTML/SVG element or [`Rect`](#rect)  \n*Default:* Parent element\n\nA rectangle that restricts the moving of the draggable element. The draggable element can not move to the outside of this rectangle even if a mouse pointer is moved to there.  \nIt can be a HTML or SVG element, or a `Rect` object like `{left: '10%', top: 20, right: '90%', height: 300}` (See [`Rect`](#rect)). The base of the `Rect` object is the current document.  \nNote that the rectangle is \"padding-box\" of the element if an element is specified. That is, it is the inside of borders of the element.\n\nThe width or height can be less than the size of the draggable element, such as zero, to disable the moving on that axis.  \nFor example, this draggable element can be moved in a horizontal direction only.\n\n```js\ndraggable.containment = {left: 20, top: 20, width: 800, height: 0};\n// See next code too.\n```\n\nWhen the moving on an axis is disabled, the draggable element is not moved in that axis direction at all. Therefore the position of the `containment` on that axis has no meaning.  \nFor example, when `0` is specified for `height` to disable the moving on that Y-axis, even if `0`, `999` or another value is specified for `top`, the draggable element is not moved to `0` or `999` in Y-axis.  \nTherefore the above example code can be replaced with:\n\n```js\ndraggable.containment = {left: 20, top: 0, width: 800, height: 0};\n// The element is not moved to `top: 0` even if it is positioned at `top: 480` now.\n```\n\nThe default is the draggable element's parent element.\n\nYou can specify a `\u003cbody\u003e` element that means all of a page, or `{left: 0, top: 0, width: '100%', height: '100%'}` means all of a document (the document height might differ from the `\u003cbody\u003e` height).\n\n### \u003ca name=\"options-snap\"\u003e\u003c/a\u003e`snap`\n\n*Type:* number, string, HTML/SVG element, [`Rect`](#rect), Array, Object or `undefined`  \n*Default:* `undefined`\n\nSee [Snap](#snap).\n\n### \u003ca name=\"options-autoscroll\"\u003e\u003c/a\u003e`autoScroll`\n\n*Type:* boolean, window, HTML element, Object or `undefined`  \n*Default:* `undefined`\n\nScroll a scrollable area when the draggable element is moved to near the edges of the area. The scrollable area can be the current window or a HTML element.\n\nFor example:\n\n```js\ndraggable.autoScroll = true;\n```\n\nIf `true` is specified, the `autoScroll` is enabled with all default options.\n\nIf a HTML element is specified, it is enabled with `{target: element}` option.\n\nOr, you can specify an Object that can have properties as following options.\n\n#### `target`\n\n*Type:* window or HTML element  \n*Default:* window\n\nA scrollable area. Maybe, that contains the [`containment`](#options-containment) or that is the `containment` itself.\n\n#### \u003ca name=\"options-speed\"\u003e\u003c/a\u003e`speed`\n\n*Type:* number or Array  \n*Default:* `[40, 200, 1000]`\n\nA number determining the speed of scrolling, pixels per second.\n\nYou can specify an Array that contains multiple values. Then, scroll it with the first speed when the draggable element was moved near the edges of the area, and scroll it with the next speed when the draggable element was moved near more..., in this way. At most, you can specify three numbers.  \nThose must be sorted in ascending order.\n\nSee [`sensitivity`](#options-sensitivity) option for lines of demarcation that switch the speed.\n\n#### \u003ca name=\"options-sensitivity\"\u003e\u003c/a\u003e`sensitivity`\n\n*Type:* number or Array  \n*Default:* `[100, 40, 0]`\n\nA number determining the starting or switching the speed, the distance from the edge of the area, in pixels.\n\nWhen you specify multiple values for [`speed`](#options-speed) option, you can specify an Array that contains multiple values. Then, scroll it with the first speed when the draggable element was moved at less than the first distance from the edges of the area, and scroll it with the next speed when the draggable element was moved at less than the next distance..., in this way. At most, you can specify three numbers.  \nThose must be sorted in descending order.\n\nSee [`speed`](#options-speed) option for the speed.\n\n#### `minX` / `maxX` / `minY` / `maxY`\n\n*Type:* number or `undefined`  \n*Default:* `undefined`\n\nA number setting a limit to scrolling. These values are returned value by `pageXOffset`, `pageYOffset`, `scrollLeft` or `scrollTop`.  \nYou can specify `0` for `maxX` or `maxY` to disable scrolling for that axis only. For example, `maxX: 0` disables horizontal scrolling only.\n\nNote that this is a limit of the `autoScroll`, not scrolling of UI. That is, user can scroll manually, regardless of this.\n\n### \u003ca name=\"options-handle\"\u003e\u003c/a\u003e`handle`\n\n*Type:* HTML/SVG element  \n*Default:* Draggable element\n\nA part element of the draggable element that receives mouse (or touch interfaces) operations. A user seizes and drags this element to move the draggable element.  \nThe default is the draggable element itself, and all of the draggable element can be seized and dragged.\n\n### \u003ca name=\"options-zIndex\"\u003e\u003c/a\u003e`zIndex`\n\n*Type:* number or boolean  \n*Default:* `9000`\n\nA [`z-index`](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) CSS property that is applied to the draggable element when it is being dragged. It is restored when the dragging finished.  \nIf `false` is specified, it is not changed.\n\n### \u003ca name=\"options-left_top\"\u003e\u003c/a\u003e`left` / `top`\n\n*Type:* number or `undefined`  \n*Default:* `undefined`\n\nThe distance between a left or top edge of the draggable element and a left or top edge of the document, in pixels.\n\nIt can be used for initial position when constructing new PlainDraggable instance. Also, it can be used to move the draggable element after constructing.  \nFor example, move it rightward:\n\n```js\ndraggable.left += 10;\n```\n\n### \u003ca name=\"options-ondrag\"\u003e\u003c/a\u003e`onDrag`\n\n*Type:* function or `undefined`  \n*Default:* `undefined`\n\nA function that is called when the draggable element was dragged. That is not moved yet.  \nIt is called even if the draggable element is not moved because [`containment`](#options-containment) or [`snap`](#options-snap) avoided the moving. Use [`onMove`](#options-onmove) if you want to do something when the draggable element was moved.\n\nIn the function, `this` refers to the current PlainDraggable instance.\n\nAn Object that has `left` and `top` properties as new position is passed to the function. These are numbers that were calculated the same as [`left` / `top`](#options-left_top) options.  \nIt is new position that the draggable element is about to go to. Therefore it might differ from a position of a mouse pointer. And also, you can change these properties.\n\nFor example:\n\n```js\ndraggable.onDrag = function(newPosition) {\n  if (newPosition.left \u003e this.rect.left) {\n    newPosition.left = this.rect.left + 48; // Move it 48px to the right forcibly.\n  }\n};\n```\n\nAlso, `snapped` property is `true` if the position was determined by the draggable element being snapped (See [Snap](#snap)).\n\nIf the function returns `false`, the moving is canceled.\n\nFor example:\n\n```js\ndraggable.onDrag = function(newPosition) {\n  return !!newPosition.snapped; // It is moved only when it is snapped.\n};\n// The `gravity` option also can be used for the same purpose.\n```\n\n### \u003ca name=\"options-onmove\"\u003e\u003c/a\u003e`onMove`\n\n*Type:* function or `undefined`  \n*Default:* `undefined`\n\nA function that is called when the draggable element was moved.  \nIt is not called when the draggable element is not moved because [`containment`](#options-containment) or [`snap`](#options-snap) avoided the moving even if the draggable element is dragged.\n\nIn the function, `this` refers to the current PlainDraggable instance.\n\nAn Object that has `left` and `top` properties is passed to the function. It might have `snapped` property too. See [`onDrag`](#options-ondrag) for those properties.  \nAnd also, `autoScroll` property is `true` if it tried scroll (See [`autoScroll`](#options-autoscroll) option). This means that the draggable element was moved to near the edges of the area, it might have been not scrolled.\n\nFor example:\n\n```js\ndraggable.onMove = function(newPosition) {\n  console.log('left: %d top: %d width: %d height: %d it is scrolling: %s'\n    // determined position that was changed by snap and onDrag\n    newPosition.left, newPosition.top,\n    this.rect.width, this.rect.height,\n    newPosition.autoScroll);\n};\n```\n\n### \u003ca name=\"options-ondragstart\"\u003e\u003c/a\u003e`onDragStart`\n\n*Type:* function or `undefined`  \n*Default:* `undefined`\n\nA function that is called when a mouse button or touch interface was pressed on the draggable element. That is not dragged yet.  \nIt is called even if the draggable element is not dragged. Use [`onDrag`](#options-ondrag) if you want to do something when the draggable element was dragged.\n\nIn the function, `this` refers to the current PlainDraggable instance.\n\nAn Object that has `clientX` and `clientY` properties pointing a position of a mouse pointer or touch interface is passed to the function. That might be [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) or first [`Touch`](https://developer.mozilla.org/en-US/docs/Web/API/Touch) object of [`TouchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent).\n\nIf the function returns `false`, the dragging is canceled.\n\nFor example:\n\n```js\ndraggable.onDragStart = function(pointerXY) {\n  if (isEar(pointerXY)) {\n    alert('Do not pull my ear!!');\n    return false;\n  }\n  return true;\n};\n```\n\n### \u003ca name=\"options-onmovestart\"\u003e\u003c/a\u003e`onMoveStart`\n\n*Type:* function or `undefined`  \n*Default:* `undefined`\n\nA function that is called when the moving the draggable element started.  \nIt is not called until the draggable element is moved even if it was dragged, and it is called at most once, while a mouse button is being pressed.\n\nIn the function, `this` refers to the current PlainDraggable instance.\n\nAn Object that has `left` and `top` properties is passed to the function. It might have `snapped` and `autoScroll` properties too. See [`onMove`](#options-onmove) for those properties.\n\n### \u003ca name=\"options-ondragend\"\u003e\u003c/a\u003e`onDragEnd`\n\n*Type:* function or `undefined`  \n*Default:* `undefined`\n\nA function that is called when a mouse button was released.  \nIt is called even if the mouse pointer was not moved.\n\nIn the function, `this` refers to the current PlainDraggable instance.\n\nAn Object that has `left` and `top` properties is passed to the function. See [`onDrag`](#options-ondrag) for those properties.  \n\n## Properties\n\n### `disabled`\n\n*Type:* boolean  \n*Default:* `false`\n\nIf `true` is specified, the draggable element stops the moving immediately, and it does not receive mouse operations.\n\n### `element`\n\n**Read-only**\n\n*Type:* HTML/SVG element\n\nThe element that was passed to [constructor](#constructor).\n\n### \u003ca name=\"properties-rect\"\u003e\u003c/a\u003e`rect`\n\n**Read-only**\n\n*Type:* [`Rect`](#rect)\n\nA [`Rect`](#rect) object to indicate current position and size of the draggable element.  \nThe base of the `Rect` object is the current document.\n\n### Accessor Properties\n\nProperties with the same name as each option to get or set following options:\n\n- [`containment`](#options-containment)\n- [`snap`](#options-snap)\n- [`autoScroll`](#options-autoscroll)\n- [`handle`](#options-handle)\n- [`zIndex`](#options-zIndex)\n- [`left` / `top`](#options-left_top)\n- [`onDrag`](#options-ondrag)\n- [`onMove`](#options-onmove)\n- [`onDragStart`](#options-ondragstart)\n- [`onMoveStart`](#options-onmovestart)\n- [`onDragEnd`](#options-ondragend)\n\nFor example:\n\n```js\ndraggable.containment = wideArea;\ndraggable.handle = newHandle;\ndraggable.left += 10;\n```\n\nYou can use [`setOptions`](#setoptions) method instead to set multiple options.\n\n### `PlainDraggable.draggableCursor` / `PlainDraggable.draggingCursor`\n\n**Static Property**\n\n*Type:* string or Array  \n*Default:* (depend on web browser)\n\n- `PlainDraggable.draggableCursor`: ![grab](grab.png) `['grab', 'all-scroll', 'move']` or `['all-scroll', 'move']` (For Webkit)\n- `PlainDraggable.draggingCursor`: ![grabbing](grabbing.png) `['grabbing', 'move']` or `'move'` (For Webkit)\n\n`PlainDraggable.draggableCursor` is a `cursor` CSS property that is applied to the [`handle`](#options-handle) element.  \n`PlainDraggable.draggingCursor` is a `cursor` CSS property that is applied to the [`handle`](#options-handle) element while a mouse button is being pressed.  \nIf an Array that contains multiple values is specified, PlainDraggable tries it with each value until any of them succeeded.\n\nNote that the allowed values of the `cursor` CSS property depend on each web browser. And also, Webkit has a bug about the `cursor` CSS property, and the bug is being ignored a long time.\n\nIf `false` is specified, it is not changed.  \nIf `false` is specified for both `PlainDraggable.draggableCursor` and `PlainDraggable.draggingCursor`, PlainDraggable does nothing to a `cursor` CSS property.\n\n### `PlainDraggable.draggableClass` / `PlainDraggable.draggingClass` / `PlainDraggable.movingClass`\n\n**Static Property**\n\n*Type:* string  \n*Default:*\n\n- `PlainDraggable.draggableClass`: `'plain-draggable'`\n- `PlainDraggable.draggingClass`: `'plain-draggable-dragging'`\n- `PlainDraggable.movingClass`: `'plain-draggable-moving'`\n\n`PlainDraggable.draggableClass` is a class name that is added to the draggable element.  \n`PlainDraggable.draggingClass` is a class name that is added to the draggable element within the period from when a mouse button is pressed until the button is released. Then, the draggable element has both classes.  \n`PlainDraggable.movingClass` is a class name that is added to the draggable element within the period from when the draggable element starts moving until a mouse button is released. Then, the draggable element has three classes.\n\nFor example:\n\n```js\nPlainDraggable.draggableClass = 'cat';\nPlainDraggable.draggingClass = 'lift';\nPlainDraggable.movingClass = 'swing';\n```\n\n## `Rect`\n\nAn Object to indicate a position and size of a rectangle, like [`DOMRect`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect).  \nIt has following properties:\n\n- `left` or `x`\n- `top` or `y`\n- `width` or `right`\n- `height` or `bottom`\n\nAlso, it is relative to a \"base\".\n\nThe `left` / `top` / `right` / `bottom` indicate distance between the left or top edge of the base and the left or top side of the rectangle. Note that `right` / `bottom` also indicate distance from the left or top edge of the base, not from the right or bottom edge.  \nThe `x` is an alias for `left`, and the `y` is an alias for `top`.  \nThe `width` / `height` indicate a size of the rectangle. The `width` is required if `right` is not specified, and the `height` is required if `bottom` is not specified.\n\nAll values are a number as pixels or string as percentage (e.g. `'30%'`).  \nThe \"base\" determines the origin of coordinates, and the number of pixels of `100%`.\n\n- When the `Rect` object is specified for the [`containment`](#options-containment) option, the base is the current document.  \n- When the `Rect` object is specified for the [`snap`](#options-snap) option, the base is determined by [`base`](#snap-base) option.  \n- When the `Rect` object is got by the [`rect`](#properties-rect) property, the base is the current document.\n\nNote that the `right` must be greater than or equal to `left`, and the `bottom` must be greater than or equal to `top`. In other words, the `width` and `height` can not be negative value. PlainDraggable does not invert these to avoid behavior that you don't want. For example, `{left: 400, right: '50%'}` is ignored when the base is resized to smaller than `800px` width.\n\nFor example, when the base width is `600px` and height is `400px`:\n- `{left: 20, top: '10%', width: '50%', bottom: 390}` indicates a rectangle that is `left: 20px; top: 40px; width: 300px; height: 350px;`, positioned relative to the top-left corner of the base.\n- `{left: '50%', top: 0, width: '50%', height: '100%'}` indicates a rectangle that is the right half of the base.\n- `{left: '10%', top: '10%', right: '90%', bottom: '90%'}` indicates a rectangle that has `10%` margin at all sides.\n\n## Snap\n\nYou can specify [`snap`](#options-snap) option.  \nThe `snap` option makes one or more \"snap-targets\". The draggable element gravitates toward a snap-target when it is moved to near the snap-target, and it sticks to the snap-target.\n\nEach snap-target can be one of the following:\n\n- Point\n- Line\n- Element or [`Rect`](#rect) object\n\nThe point acts on both a horizontal direction and a vertical direction, and the line acts on either direction.  \nThe point and line are the basic of snap-target. The element and `Rect` object create multiple lines.\n\nSingle value creates a point that the value indicates both X and Y coordinates of the point. Then a top-left corner (default) of the draggable element snaps to this point.  \nThis is simplest case.\n\n```js\ndraggable.snap = 60; // Point (X: 60px, Y: 60px)\n```\n\nA value as coordinate is a number as pixels or string as percentage (e.g. `'30%'`). The percentage is calculated the same as the `x` and `y` of [`Rect`](#rect).  \nFor example, this indicates the center of the base.\n\n```js\ndraggable.snap = '50%'; // Point (X: 50%, Y: 50%) i.e. the center\n```\n\nAn Object that has `x` and `y` properties creates a point that the properties indicate coordinates of the point.\n\n```js\ndraggable.snap = {x: 160, y: '40%'}; // Point (X: 160px, Y: 40%)\n```\n\nThat is, a value that does not have both `x` and `y` is considered as a point that the value indicates the same value for the `x` and `y`. For example, `30` is considered as `{x: 30, y: 30}`.\n\nAn Object that has either one of `x` and `y` properties creates a line that the property indicates a coordinate on the axis. Then two sides (default) of the draggable element along a direction of this line snap to this line.\n\n```js\ndraggable.snap = {x: 130}; // Vertical Line (from top edge to bottom edge)\n```\n\nBy default, the line has full length of size of the base.  \nThe other property can be an Object that has one or both of `start` and `end` properties that indicate a coordinate on the axis. This Object indicates the range on the axis for the line.\n\n```js\ndraggable.snap = {x: 130, y: {start: 5, end: '50%'}}; // Vertical Line\n```\n\nNote that the `end` must be greater than or equal to `start`. PlainDraggable does not invert these to avoid behavior that you don't want. For example, `x: {start: 400, end: '50%'}` is ignored when the base is resized to smaller than `800px` width.\n\nA `step` property of the Object copies the point or line repeatedly at specified intervals. This may be called \"Grid\". It can be also grid on only either one of X and Y axis.  \nIts value is a number as pixels or string as percentage (e.g. `'30%'`). The percentage is calculated the same as the `width` or `height` of [`Rect`](#rect).\n\n```js\n// Consecutive Points at 40px intervals, on X and Y\ndraggable.snap = {step: 40};\n// As previously mentioned, this is considered as {x: {step: 40}, y: {step: 40}}.\n```\n\n```js\n// Consecutive Points at X: 10px and Y: 20px intervals\ndraggable.snap = {x: {step: 10}, y: {step: 20}};\n```\n\n```js\n// Consecutive Horizontal Lines at 25% intervals (from left edge to right edge)\ndraggable.snap = {y: {step: '25%'}};\n```\n\n```js\n// 4 Consecutive Horizontal Lines (from left edge to right edge)\ndraggable.snap = {y: {step: '20%', end: '60%'}};\n```\n\n```js\n// 4 Consecutive Horizontal Lines (300px length)\ndraggable.snap = {x: {end: 300}, y: {step: '20%', end: '60%'}};\n```\n\nAn element or a [`Rect`](#rect) object creates four lines that overlap with edges of its rectangle.\n\n```js\ndraggable.snap = document.getElementById('snap-box'); // Element\n```\n\n```js\ndraggable.snap = {left: '10%', top: 0, width: '80%', height: 280}; // Rect object\n```\n\nThe base of the `Rect` object is determined by [`base`](#snap-base) option.\n\nYou can specify options for the snap-target via properties of the Object that is passed to the `snap` option, or an Object instead of something that is not Object. For example, you can specify `{x: 80, y: 80, gravity: 30}` instead of `80` to specify the [`gravity`](#snap-gravity) option.  \nAlso, you can specify an Array that contains multiple snap-targets.\n\nFor options and more details, refer to the following.\n\n### Structure and Abbreviation\n\nActually, you saw shortened structure of a `snap` option in the above.  \nIts basic structure is here:\n\n**`SnapOptions`:**\n\n```js\n{\n  targets: [snapTarget1, snapTarget2...], // Array containing `SnapTarget`s\n  // Common options for all `SnapTarget`s in the `targets`\n  option1: optionValue1,\n  option2: optionValue2,\n    :\n}\n```\n\n**`SnapTarget`:**\n\n```js\n{\n  x: xValue, // number, string or Object\n  y: yValue,\n  boundingBox: elementOrRect, // Element or Rect object\n  // Options for this `SnapTarget`, that override each common option\n  option1: optionValue1,\n  option2: optionValue2,\n    :\n}\n```\n\nThe `x` and `y` can be one of the following:\n\n- **Coordinate:**  \nA value that indicates a coordinate on the axis.\n- **Range:**  \nAn Object that indicates a range on the axis, that can have `start` and `end` properties that are each coordinate on the axis.\n- **Repeated Coordinates:**  \nAn Object that indicates repeated coordinates on the axis, that has `step` property that is interval between repeated coordinates. It can have `start` and `end` properties as a range.\n\nAll these values are a number as pixels or a string as percentage (e.g. `'30%'`). The `step` as percentage is calculated the same as the `width` or `height` of [`Rect`](#rect), and the other values as percentage are calculated the same as the `x` or `y` of that.\n\nThe combination of the `x` and `y` determines what the `SnapTarget` indicates.\n\n- If both `x` and `y` indicate each coordinate, this `SnapTarget` indicates a point.\n- If either one of `x` and `y` indicates a coordinate and the other indicates a range, this `SnapTarget` indicates a line.\n- If either one of `x` and `y` indicates a coordinate and the other indicates repeated coordinates, this `SnapTarget` indicates consecutive points arranged linearly.\n- If both `x` and `y` indicate repeated coordinates, this `SnapTarget` indicates consecutive points arranged in the horizontal and vertical directions.\n- If either one of `x` and `y` indicates a range and the other indicates repeated coordinates, this `SnapTarget` indicates consecutive parallel lines.\n- If both `x` and `y` indicate each range, this `SnapTarget` indicates four lines as a rectangle. Unlike `boundingBox`, [`edge`](#snap-edge) option is not applied.\n\nAn **important point**, the default for the `x` and `y` is a range, and the default for the `start` is `0`, and the default for the `end` is `'100%'`.  \nTherefore, you can specify either one of the `x` and `y` to indicate a line. For example, `{y: 30}` indicates a horizontal line. The `x` having either one or both of the `start` and `end` is required only when the line should be shortened.  \nAlso, you can specify only the `step` to indicate consecutive points or consecutive lines. For example, `{y: {step: 50}}` indicates consecutive horizontal lines.\n\nThe `boundingBox` is used instead of the `x` and `y`, to indicate a rectangle.  \nYou can specify an element or [`Rect`](#rect) object, then this `SnapTarget` indicates four lines that overlap with edges of its rectangle.\n\nYou can specify one or more `SnapTarget`s for the `targets` Array of `SnapOptions`.  \nAnd you can specify one or more options in both `SnapTarget` and `SnapOptions`. The options in `SnapTarget` override each option in `SnapOptions`.  \nFor example:\n\n```js\ndraggable.snap = {\n  targets: [\n    {x: 100},\n    {x: 200, gravity: 60},\n    {x: 300}\n  ],\n  gravity: 50,\n  center: true\n};\n```\nA `50` is applied to the [`gravity`](#snap-gravity) option of first and third snap-targets.  \nA `60` is applied to the [`gravity`](#snap-gravity) option of second snap-target only.  \nA `true` is applied to the [`center`](#snap-center) option of all three snap-targets.\n\n#### Abbreviation\n\nAs above, you can specify the `snap` option in detail. Or, you can also specify it simply as follows.\n\nYou can specify something for the `SnapTarget` directly. It is considered as both `x` and `y`, or `boundingBox`.  \nFor example, the following two codes work same:\n\n```js\ndraggable.snap = {\n  targets: [\n    100,\n    document.getElementById('snap-box'),\n    {x: 20, y: 0, width: 400, height: 200}\n  ]\n};\n```\n\n```js\ndraggable.snap = {\n  targets: [\n    {x: 100, y: 100},\n    {boundingBox: document.getElementById('snap-box')},\n    {boundingBox: {x: 20, y: 0, width: 400, height: 200}}\n  ]\n};\n```\n\nYou can specify single `SnapTarget` for the `targets` directly instead of an Array.  \nFor example:\n\n```js\ndraggable.snap = {\n  targets: 100 // The same as [100] and [{x: 100, y: 100}]\n};\n```\n\nYou can specify the `targets` for the `SnapOptions` directly.  \nFor example:\n\n```js\ndraggable.snap = 100;\n// The same as {targets: 100} and {targets: [100]} and {targets: [{x: 100, y: 100}]}\n\ndraggable.snap = [100, {x: 200, gravity: 60}]; // 2 targets\n```\n\n### Options for Snap\n\nThese are specified in the [`SnapOptions` and `SnapTarget`](#structure-and-abbreviation).\n\n#### \u003ca name=\"snap-gravity\"\u003e\u003c/a\u003e`gravity`\n\n*Type:* number  \n*Default:* `20`\n\nDistance from the draggable element, in pixels.  \nThe draggable element gravitates toward the snap-target when the distance becomes less than or equal to this.\n\nTo avoid staying between \"[repeated coordinates](#structure-and-abbreviation)\", you can specify a number greater than or equal to the half of the `step`. Or, [`onDrag`](#options-ondrag) option also can be used for the same purpose.\n\n#### \u003ca name=\"snap-corner\"\u003e\u003c/a\u003e`corner`\n\n**For snap-target as point**\n\n*Type:* string  \n*Default:* `'tl'`\n\nOne or more corners of the draggable element that gravitate toward the snap-target.  \nIt is a value that indicates a corner or a list of the values separated by space or comma.\n\nAllowed values are:\n\n- `'top-left'` (Alias: `'tl'`, `'left-top'`, `'lt'`)\n- `'top-right'` (Alias: `'tr'`, `'right-top'`, `'rt'`)\n- `'bottom-left'` (Alias: `'bl'`, `'left-bottom'`, `'lb'`)\n- `'bottom-right'` (Alias: `'br'`, `'right-bottom'`, `'rb'`)\n- `'all'` (The same as `'tl tr bl br'`)\n\n#### \u003ca name=\"snap-side\"\u003e\u003c/a\u003e`side`\n\n**For snap-target as line**\n\n*Type:* string  \n*Default:* `'both'`\n\nOne or more sides of the draggable element that gravitate toward the snap-target.  \nIt is a value that indicates a side or a list of the values separated by space or comma.\n\nAllowed values are:\n\n- `'start'`: Indicate a left side of the draggable element if the snap-target is vertical line, otherwise a top side.\n- `'end'`: Indicate a right side of the draggable element if the snap-target is vertical line, otherwise a bottom side.\n- `'both'` (The same as `'start end'`)\n\n#### \u003ca name=\"snap-center\"\u003e\u003c/a\u003e`center`\n\n**For snap-target as point or line**\n\n*Type:* boolean  \n*Default:* `false`\n\nIf `true` is specified, the center of the draggable element gravitates toward the snap-target. The [`corner`](#snap-corner) and [`side`](#snap-side) options are ignored.\n\n#### \u003ca name=\"snap-edge\"\u003e\u003c/a\u003e`edge`\n\n**For snap-target as element or [`Rect`](#rect) object**\n\n*Type:* string  \n*Default:* `'both'`\n\nOne or more sides of each edge of the rectangle that the draggable element gravitates toward. The rectangle is that of the element or [`Rect`](#rect) object that is specified for the [`boundingBox`](#structure-and-abbreviation).  \nIt is a value that indicates a side or a list of the values separated by space or comma.\n\nAllowed values are:\n\n- `'inside'`\n- `'outside'`\n- `'both'` (The same as `'inside outside'`)\n\n#### \u003ca name=\"snap-base\"\u003e\u003c/a\u003e`base`\n\n*Type:* string  \n*Default:* `'containment'`\n\nA base for the [`Rect`](#rect) object. The [`x` and `y`](#structure-and-abbreviation) also are relative to this.  \n\nAllowed values are:\n\n- `'containment'`: Indicate the element or [`Rect`](#rect) object that is specified for the [`containment`](#options-containment) option.\n- `'document'`: Indicate the current document.\n\n## Development\n\nIf you want to use a NPM package in development mode such as importing it to your app with e.g. Webpack, you have to install the package and `devDependencies` packages in accordance with customary practice.\n\nFor example:\n```\nnpm i plain-draggable\ncd node_modules/plain-draggable\nnpm i\n```\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanseki%2Fplain-draggable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanseki%2Fplain-draggable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanseki%2Fplain-draggable/lists"}