{"id":22160382,"url":"https://github.com/rozek/dommali-utils","last_synced_at":"2026-04-18T02:07:26.169Z","repository":{"id":65179092,"uuid":"585800141","full_name":"rozek/dommali-utils","owner":"rozek","description":"varoius utility functions for DOMMaLi","archived":false,"fork":false,"pushed_at":"2023-01-15T05:18:25.000Z","size":248,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-01T21:54:45.116Z","etag":null,"topics":["drag","drag-and-drop","event-handling","javascript","jquery","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/rozek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2023-01-06T05:25:38.000Z","updated_at":"2023-01-06T07:33:48.000Z","dependencies_parsed_at":"2023-02-09T21:10:15.743Z","dependency_job_id":null,"html_url":"https://github.com/rozek/dommali-utils","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rozek/dommali-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rozek%2Fdommali-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rozek%2Fdommali-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rozek%2Fdommali-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rozek%2Fdommali-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rozek","download_url":"https://codeload.github.com/rozek/dommali-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rozek%2Fdommali-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31953517,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T00:39:45.007Z","status":"online","status_checked_at":"2026-04-18T02:00:07.018Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["drag","drag-and-drop","event-handling","javascript","jquery","typescript"],"created_at":"2024-12-02T04:07:39.474Z","updated_at":"2026-04-18T02:07:26.131Z","avatar_url":"https://github.com/rozek.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dommali-utils #\n\nvarious utility functions for DOMMaLi\n\n`dommali-utils` extends the [dommali](https://github.com/rozek/dommali) library by several utility methods which implement various event handling functions, e.g., for element dragging or non-native drag-and-drop.\n\nThe idea behind `dommali-utils` is to give a programmer exactly that amount of control (s)he needs - and offer ready-made implementations for anything else. E.g., a typical usage may look as follows (see [JS Bin](https://jsbin.com/tuzituk) for a live demo):\n\n```\n   const $ = dommali\n   $(() =\u003e {\n     $(document.body).provideSimpleDraggingFor('.Dialog',{\n       onlyFrom:'.Titlebar', neverFrom:'.CloseButton',\n       rightLimit:30, bottomLimit:30\n     })\n   })\n```\n\nThis snippet makes all `\u003cdiv/\u003e`s with the CSS class `Dialog` draggable from their titlebar. They may be dragged around within their whole parent but only so far that a small part of the titlebar always remains visible (and therefore accessible).\n\n\u003e Please note, that this module is currently under active development - do not expect a stable release before end of January 2023\n\n\n\n## API ##\n\n### Dragging Recognizer ###\n\n`recognizeDragging` and `recognizeDraggingFor` install event handlers which listen for PointerEvents, recognize dragging gestures and trigger matching `dragging-started`, `dragging-continued`, `dragging-finished` and `dragging-aborted` events. These may then be listened for in order to implement the actual element dragging (see corresponding examples in the Programming Manual).\n\nDragging recognizers support the following `DraggingOptions` - all of them are optional:\n\n* **`onlyFrom:string`**\u003cbr\u003eif given, only PointerEvents originating from an inner element matching the CSS selector `onlyFrom` will be considered, all others will be ignored. `onlyFrom` may be combined with `neverFrom`\n* **`neverFrom:string`**\u003cbr\u003eif given, PointerEvents originating from an inner element matching the CSS selector `neverFrom` will be ignored. `neverFrom` may be combined with `onlyFrom`\n* **`initialDirection:DraggingDirection`**\u003cbr\u003emay be set to `x`, `y` or `both`. When set to `x` or `y`, dragging will only be started if the pointer has moved in the given direction at the moment dragging will be recognized (as given by `minOffsetX` and `minOffsetY`) - otherwise dragging will be ignored\n* **`minOffsetX:number`**\u003cbr\u003eif set to 0, dragging immediately starts with the initial `pointerdown` event. When set to a value \u003e 0, `dragging-started` will be delayed until the pointer has moved at least `minOffsetX` or `minOffsetY` pixels from the point reported by `pointerdown` (whatever comes first)\n* **`minOffsetY:number`**\u003cbr\u003eif set to 0, dragging immediately starts with the initial `pointerdown` event. When set to a value \u003e 0, `dragging-started` will be delayed until the pointer has moved at least `minOffsetX` or `minOffsetY` pixels from the point reported by `pointerdown` (whatever comes first)\n* **`Easing:number|boolean`**\u003cbr\u003eif set to a value between 0 and 1 (exclusively), dragged elements are given some \"moment of inertia\". This means that dragged elements with a velocity \u003e 0 at the moment of a `pointerup` event will continue to move in their last direction (and trigger `dragging-continued` events) until (simulated) \"friction\" stops them. The extra events will be triggered every 100ms, and from one event to the next the dragged object's velocity (measured in pixels per second) will be multiplied with the given `Easing` factor until it falls below 10px/s. `dragging-finished` will only be triggered after the dragged elements have stopped moving\n* **`stopPropagation:boolean`**\u003cbr\u003eif set to `true`, further propagation of intercepted PointerEvents will be stopped - otherwise they may \"bubble\" as usual\n* **`stopImmediatePropagation:boolean`**\u003cbr\u003eif set to `true`, further handling and propagation of intercepted PointerEvents will be stopped\n* **`Extras:any`**\u003cbr\u003eis an optional, user-defined value which is passed unmodified along any `dragging-xxx` event and may be used to differentiate between kinds of dragging within the same event handler\n\n#### Element-specific Recognizer ####\n\nThe following methods may be applied to `dommali` objects which shall become \"draggable\" in their own specific way.\n\nAt most one specific recognizer may be installed per `dommali` object.\n\n* **`recognizesDragging ():boolean`**\u003cbr\u003ereturns `true` if a specific dragging recognizer is currently installed in `this` dommali object - of `false` otherwise\n* **`recognizeDragging (Options?:DraggingOptions):DOMMaLi`**\u003cbr\u003einstalls a specific dragging recognizer with the (optionally) given `Options` in `this` dommali object (see above for a description of available `DraggingOptions`). If there is already a specific recognizer installed in `this` object, it is implicitly uninstalled before installing the new one (using `ignoreDragging`)\n* **`ignoreDragging ():DOMMaLi`**\u003cbr\u003euninstalls the currently installed specific dragging recognizer from `this` dommali object - it is ok to call `ignoreDragging` even if such a recognizer is actually missing\n\n#### Delegated Recognizer ####\n\nThe following methods use delegated event handlers for dragging recognition. They detect dragging gestures for any element matching a given `Selector` and trigger corresponding events at the `dommali` objects the methods have been applied to. Delegated event handlers reduce the implementation effort if multiple elements should be treated in a similar way.\n\nMultiple delegated recognizers with different selectors may be installed in the same `dommali` object - but at most one per selector.\n\n* **`recognizesDraggingFor (Selector:string):boolean`**\u003cbr\u003ereturns `true` if a delegated dragging recognizer for elements matching the given `Selector` is currently installed in `this` dommali object - of `false` otherwise\n* **`recognizeDraggingFor (Selector:string, Options?:DraggingOptions):DOMMaLi`**\u003cbr\u003einstalls a delegated dragging recognizer with the (optionally) given `Options` for elements matching the given `Selector` in `this` dommali object (see above for a description of available `DraggingOptions`). If there is already a recognizer with the same `Selector` installed in `this` object, it is implicitly uninstalled before installing the new one (using `ignoreDraggingFor(Selector)`)\n* **`ignoreDraggingFor (Selector:string):DOMMaLi`**\u003cbr\u003euninstalls the currently installed delegated dragging recognizer for elements matching the given `Selector` from `this` dommali object - it is ok to call `ignoreDraggingFor` even if such a recognizer is actually missing\n\n### Simple Dragging ###\n\nBased on the \"Dragging Recognizers\" described above, `provideSimpleDragging` and `provideSimpleDraggingFor` offer directly usable implementations for elements which may be dragged around within their parents.\n\nThese implementations may be customized using the following `simpleDraggingOptions` which extend the `DraggingOptions` already mentioned above - again, all settings are optional:\n\n* **`leftLimit:number`**\u003cbr\u003eif specified, `leftLimit` keeps the left edge of a dragged element the given number of pixels away from the left edge of its parent (in the same way as the CSS property `left` positions a DOM element within its offset parent)\n* **`topLimit:number`**\u003cbr\u003eif specified, `topLimit` keeps the top edge of a dragged element the given number of pixels away from the top edge of its parent (in the same way as the CSS property `top` positions a DOM element within its offset parent)\n* **`rightLimit:number`**\u003cbr\u003eif specified, `rightLimit` keeps the left edge of a dragged element the given number of pixels away from the right edge of its parent (in the same way as the CSS property `right` positions a DOM element within its offset parent)\n* **`bottomLimit:number`**\u003cbr\u003eif specified, `bottomLimit` keeps the top edge of a dragged element the given number of pixels away from the bottom edge of its parent (in the same way as the CSS property `bottom` positions a DOM element within its offset parent)\n\nIf `provideSimpleDragging` or `provideSimpleDraggingFor` are applied without previously installing a corresponding dragging recognizer, such a recognizer is implicitly installed using the given options.\n\n#### Element-specific Dragging ####\n\n* **`provideSimpleDragging (Options?:simpleDraggingOptions):void`**\u003cbr\u003e\n\n#### Delegated Dragging ####\n\n* **`provideSimpleDraggingFor (Selector:string, Options?:simpleDraggingOptions):void`**\u003cbr\u003e\n\n\n\n\n\n## Programming Manual ##\n\n### Continuous Dragging ###\n\n\u003cimg src=\"img/continuousDragging.png\" width=300 height=200 align=\"right\"\u003e\n\nThe first example (see [JSBin](https://jsbin.com/fakelor) for a live demo) illustrates how to install a dragging recognizer in a `\u003cdiv/\u003e` called `#Arena` (in order to make all inner elements with CSS class `Circle` draggable) and then to listen for `dragging-xxx` events in order to implement the actual dragging. The example does not offer much functionality but may be used as a starting point for more complex implementations (as shown in the next example).\n\nPlease note the use of function and fat-arrow literals depending on the intended use of the current `this` object.\n\n```\n$('#Arena').recognizeDraggingFor('.Circle', { minOffsetX:4, minOffsetY:4 })\n$('#Arena').on('dragging-started', '.Circle', async function (\n  Event, Extras, curX,curY, StartX,StartY\n) {\n  let $Draggable = $(Event.target)\n  let $Container = $Draggable.parent()\n\n  let initialPosition = $Draggable.positionInParent()\n  let OffsetX = initialPosition.left-StartX\n  let OffsetY = initialPosition.top -StartY\n\n  await this.repeatUntil('dragging-finished','dragging-aborted',async () =\u003e {\n    $Draggable.css({ left:(curX+OffsetX)+'px', top:(curY+OffsetY)+'px' })\n\n    Event = await this.waitFor('dragging-continued','dragging-finished','dragging-aborted')\n    if (Event.type !== 'dragging-aborted') {\n      [Extras,curX,curY] = $.extraParametersOfEvent(Event)\n    }\n  })\n\n  if (Event.type === 'dragging-aborted') {\n    $Draggable.css({ left:initialPosition.left+'px', top:initialPosition.top+'px' })\n  }\n})\n```\n\n### Rastered Dragging ###\n\n\u003cimg src=\"img/rasteredDragging.png\" width=300 height=200 align=\"right\"\u003e\n\nThe second example (see [JSBin](https://jsbin.com/dihibil) for a live demo) illustrates how to add a custom feature to the plain dragging shown above. In this case, the top left position of draggable elements is simply restricted to the intersection points of a 20x20 grid:\n\n```\n $('#Arena').recognizeDraggingFor('.Square', { minOffsetX:4, minOffsetY:4 })\n $('#Arena').on('dragging-started', '.Square', async function (\n   Event, Extras, curX,curY, StartX,StartY\n ) {\n   let $Draggable = $(Event.target)\n   let $Container = $Draggable.parent()\n\n   let initialPosition = $Draggable.positionInParent()\n   let OffsetX = initialPosition.left-StartX\n   let OffsetY = initialPosition.top -StartY\n\n   await this.repeatUntil('dragging-finished','dragging-aborted',async () =\u003e {\n     let x = 20*Math.round((curX+OffsetX)/20)\n     let y = 20*Math.round((curY+OffsetY)/20)\n     $Draggable.css({ left:x+'px', top:y+'px' })\n\n     Event = await this.waitFor('dragging-continued','dragging-finished','dragging-aborted')\n     if (Event.type !== 'dragging-aborted') {\n       [Extras,curX,curY] = $.extraParametersOfEvent(Event)\n     }\n   })\n\n   if (Event.type === 'dragging-aborted') {\n     $Draggable.css({ left:initialPosition.left+'px', top:initialPosition.top+'px' })\n   }\n })\n```\n\n### Continuous Slider ###\n\n\u003cimg src=\"img/continuousSlider.png\" width=160 height=60\u003e\n\n(see [JSBin](https://jsbin.com/junujug) for a live demo)\n\n```\n  const $ = dommali\n  $(() =\u003e {\n    $(document.body).recognizeDraggingFor('.Slider', { alwaysFrom:'.Slider-Knob' })\n    $(document.body).on('dragging-started', '.Slider', async function (\n      Event, Extras, curX,curY, StartX,StartY\n    ) {\n      let $Draggable = $(Event.target).find('.Slider-Knob')\n      let $Container = $(Event.target)\n\n      let initialPosition = $Draggable.positionInParent()\n      let OffsetX = initialPosition.left-StartX\n\n      await this.repeatUntil('dragging-finished','dragging-aborted',async () =\u003e {\n        let x = Math.max(0,Math.min(curX+OffsetX,$Container.width()-20))\n        $Draggable.css('left', x+'px')\n\n        Event = await this.waitFor('dragging-continued','dragging-finished','dragging-aborted')\n        if (Event.type !== 'dragging-aborted') {\n          [Extras,curX,curY] = $.extraParametersOfEvent(Event)\n        }\n      })\n\n      if (Event.type === 'dragging-aborted') {\n        $Draggable.css('left',initialPosition.left+'px')\n      }\n    })\n  })\n```\n\n### Window Dragging ###\n\n\u003cimg src=\"img/WindowDragging.png\" width=300 height=200 align=\"right\"\u003e\n\nThe following example (see [JSBin](https://jsbin.com/tuzituk) for a live demo) illustrates how to make all elements with the CSS class `Dialog` draggable within their parent (which should usually be the whole document body, but is restricted to a given `\u003cdiv/\u003e` in the live demo in order to show the `rightLimit` and `bottomLimit` options):\n\n```\nconst $ = dommali\n$(() =\u003e {\n  $(document.body).provideSimpleDraggingFor('.Dialog',{\n    onlyFrom:'.Titlebar', neverFrom:'.CloseButton',\n    rightLimit:30, bottomLimit:30\n  })\n})\n```\n\n\n## Build Instructions ##\n\nYou may easily build this package yourself.\n\nJust install [NPM](https://docs.npmjs.com/) according to the instructions for your platform and follow these steps:\n\n1. either clone this repository using [git](https://git-scm.com/) or [download a ZIP archive](https://github.com/rozek/dommali-utils/archive/refs/heads/main.zip) with its contents to your disk and unpack it there \n2. open a shell and navigate to the root directory of this repository\n3. run `npm install` in order to install the complete build environment\n4. execute `npm run build` to create a new build\n\nYou may also look into the author's [build-configuration-study](https://github.com/rozek/build-configuration-study) for a general description of his build environment.\n\n## License ##\n\n[MIT License](LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frozek%2Fdommali-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frozek%2Fdommali-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frozek%2Fdommali-utils/lists"}