├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist └── index.d.ts ├── karma.conf.cjs ├── lib ├── attr.js ├── classes.js ├── clear.js ├── closest.js ├── delegate.js ├── domify.js ├── event.js ├── index.js ├── matches.js ├── query.js ├── remove.js └── style.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── test ├── .eslintrc ├── attr.js ├── bind.js ├── classes.js ├── clear.js ├── closest.js ├── delegate.js ├── integration │ └── ts.spec.ts ├── matches.js ├── query.js ├── remove.js └── style.js └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:bpmn-io/browser" 3 | } -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [ push, pull_request ] 3 | jobs: 4 | Build: 5 | 6 | strategy: 7 | matrix: 8 | os: [ ubuntu-latest ] 9 | node-version: [ 20 ] 10 | 11 | runs-on: ${{ matrix.os }} 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Use Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | cache: 'npm' 21 | - name: Install dependencies 22 | run: npm ci 23 | - name: Build 24 | env: 25 | TEST_BROWSERS: ChromeHeadless,Firefox 26 | run: xvfb-run npm run all 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to [min-dom](https://github.com/bpmn-io/min-dom) are documented here. We use [semantic versioning](http://semver.org/) for releases. 4 | 5 | ## Unreleased 6 | 7 | ___Note:__ Yet to be released changes appear here._ 8 | 9 | ## 5.1.1 10 | 11 | * `CHORE`: revert to basic `clear` 12 | 13 | ## 4.2.1 14 | 15 | * `CHORE`: revert to basic `clear` 16 | 17 | ## 5.1.0 18 | 19 | * `FEAT`: optimize `clear` 20 | * `FEAT`: use native `closest` 21 | * `FIX`: properly handle missing `element` in `matches` 22 | 23 | ## 4.2.0 24 | 25 | * `FEAT`: optimize `clear` 26 | * `FEAT`: use native `closest` 27 | * `FIX`: properly handle missing `element` in `matches` 28 | 29 | ## 5.0.0 30 | 31 | * `CHORE`: turn into ES module 32 | * `CHORE`: drop UMD distribution 33 | * `CHORE`: do not inline stable external dependencies 34 | 35 | ### Breaking Changes 36 | 37 | * This is an ES module only package now. Consume in modern browsers. 38 | * Dropped UMD distribution. Consume as an ES module 39 | 40 | ## 4.1.0 41 | 42 | * `DEPS`: update to `component-event@0.2.1` ([#19](https://github.com/bpmn-io/min-dom/pull/19)) 43 | 44 | ## 4.0.3 45 | 46 | * `DEPS`: bump to `min-dash@4` 47 | 48 | ## 4.0.2 49 | 50 | * `FIX`: correctly reexport `component-event` ([#18](https://github.com/bpmn-io/min-dom/pull/18)) 51 | * `CHORE`: generate sourcemaps 52 | 53 | ## 4.0.1 54 | 55 | * `CHORE`: remove unnecessary deps, script, and .babelrc 56 | 57 | ## 4.0.0 58 | 59 | * `FEAT`: change library target to `ES2018` 60 | * `FEAT`: drop `matches` shim 61 | * `FEAT`: drop polyfills for browser not supporting `ES2018` 62 | 63 | ### Breaking Changes 64 | 65 | * You have to shim `Element#matches` 66 | * Target syntax is `ES2018`. Transpile the code base to target `< ES2018`. 67 | * Polyfills for browsers not supporting `ES2018` are dropped (e.g. Element.classList). 68 | 69 | ## 3.2.1 70 | 71 | * `FIX`: expose correct types ([#14](https://github.com/bpmn-io/min-dom/issues/14)) 72 | 73 | ## 3.2.0 74 | 75 | * `FEAT`: add `assignStyle` utility 76 | 77 | ## 3.1.3 78 | 79 | * `FIX`: insource delegate-events ([#8](https://github.com/bpmn-io/min-dom/issues/8)) 80 | 81 | ## 3.1.2 82 | 83 | * `FIX`: make `closest` work inside shadow DOM ([#5](https://github.com/bpmn-io/min-dom/issues/5)) 84 | 85 | ## 3.1.1 86 | 87 | * `FIX`: correct TypeScript export definitions 88 | 89 | ## 3.1.0 90 | 91 | * `FEAT`: add TypeScript definitions 92 | 93 | ## 3.0.0 94 | 95 | ### Breaking Changes 96 | 97 | * `FIX`: remove browser field again; it confuses modern module bundlers. This partially reverts `v2.1.0` 98 | 99 | ## 2.2.0 100 | 101 | * `CHORE`: mark utils as side-effect free via `sideEffects: false` 102 | 103 | ## 2.1.0 104 | 105 | * `CHORE`: add `browser` field 106 | 107 | ## 2.0.0 108 | 109 | ### Breaking Changes 110 | 111 | * `CHORE`: expose utils via main entry point only; use imports / destructuring to access them: 112 | 113 | ```javascript 114 | import { bind } from 'min-dom'; 115 | ``` 116 | 117 | * `CHORE`: expose `query.all()` as `queryAll` 118 | 119 | ### Other Improvements 120 | 121 | * `CHORE`: migrate libray to ES6 122 | * `CHORE`: build and publish ES6, CommonJS and UMD distributions 123 | 124 | ## 1.0.0 125 | 126 | * `CHORE`: remove `component-query` and rely on native `querySelector(All)` 127 | 128 | ## ... 129 | 130 | Check `git log` for earlier history. 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nico Rehwaldt 4 | Copyright (c) 2015-present camunda Services GmbH 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # min-dom 2 | 3 | [![CI](https://github.com/bpmn-io/min-dom/workflows/CI/badge.svg)](https://github.com/bpmn-io/min-dom/actions?query=workflow%3ACI) 4 | 5 | A minimal dom utility toolbelt. Library friendly and based on utilities provided by [component](https://github.com/component). 6 | 7 | 8 | ## Footprint 9 | 10 | This library is tiny (`2Kb` in size) and still exposes all fundamental utilities: 11 | 12 | ```bash 13 | $ npm run bundle 14 | $ gzip dist/index.js 15 | $ du -b dist/*.gz 16 | 2351 index.js.gz 17 | ``` 18 | 19 | 20 | ## Features 21 | 22 | The library exposes the following tiny dom helpers: 23 | 24 | * `assignStyle` - add inline styles to a node 25 | * `attr` - get and set node attributes 26 | * `classes` - class name helper 27 | * `clear` - remove children from a node 28 | * `closest` - get the closest parent by selector; 29 | * `delegate` - event deletation support; [delegate-events](https://www.npmjs.com/package/delegate-events) 30 | * `domify` - html to elements; [domify](https://github.com/component/domify) 31 | * `event` - event binding; [component-event](https://github.com/component/event) 32 | * `matches` - selector match check 33 | * `query` - native selector query support 34 | * `remove` - detach a node from its parent 35 | 36 | 37 | ## Related 38 | 39 | * [min-dash](https://github.com/bpmn-io/min-dash) - minimal lodash inspired utility toolbelt 40 | * [tiny-svg](https://github.com/bpmn-io/tiny-svg) - tiny SVG utility toolbelt 41 | 42 | 43 | ## License 44 | 45 | MIT 46 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Set attribute `name` to `val`, or get attr `name`. 3 | * 4 | * @param {Element} el 5 | * @param {String} name 6 | * @param {String} [val] 7 | * @api public 8 | */ 9 | export function attr(el: T, name: string): string; 10 | export function attr(el: T, name: string, val: null): void; 11 | export function attr(el: T, name: string, val: string): T; 12 | export function attr(el: T, name: string, val?: string | null): void | string | T; 13 | 14 | /** 15 | * Wrap `el` in a `ClassList`. 16 | * 17 | * @param {Element} el 18 | * @return {ClassList} 19 | * @api public 20 | */ 21 | export function classes(el: T): ClassList; 22 | 23 | export class ClassList { 24 | public list: T["classList"]; 25 | public el: T; 26 | constructor(el: T); 27 | 28 | add(name: string): this; 29 | remove(name: string | RegExp): this; 30 | 31 | removeMatching(re: RegExp): this; 32 | 33 | toggle(name: string, force?: boolean): this; 34 | 35 | array(): string[]; 36 | 37 | has(name: string): boolean; 38 | contains(name: string): boolean; 39 | } 40 | 41 | /** 42 | * Remove all children from the given element. 43 | */ 44 | export function clear(el: T): T; 45 | 46 | export type CSSSelector = string; 47 | export function closest(element: Element, selector: CSSSelector, checkYoSelf?: boolean): Element; 48 | 49 | export namespace delegate { 50 | export function bind(el: T, selector: CSSSelector, type: string, callback: (el: T, e: Event) => void, capture?: boolean): Function; 51 | export function unbind(el: T, type: string, callback: (el: T, e: Event) => void, capture?: boolean): void; 52 | } 53 | 54 | export function domify(dom: string, doc?: HTMLDocument): HTMLElement; 55 | 56 | export namespace event { 57 | export function bind(el: EventTarget, type: string, fn: CType, capture?: boolean): CType; 58 | export function unbind(el: EventTarget, type: string, fn: CType, capture?: boolean): CType; 59 | } 60 | 61 | export function matches(el: HTMLElementTagNameMap[K], selector: K): el is HTMLElementTagNameMap[K]; 62 | export function matches(el: HTMLElement, selector: CSSSelector): boolean; 63 | 64 | export function query(selectors: K, el?: HTMLElement): HTMLElementTagNameMap[K] | null; 65 | export function query(selectors: K, el?: HTMLElement): SVGElementTagNameMap[K] | null; 66 | export function query(selectors: string, el?: HTMLElement): E | null; 67 | 68 | export function queryAll(selectors: K, el?: HTMLElement): NodeListOf; 69 | export function queryAll(selectors: K, el?: HTMLElement): NodeListOf; 70 | export function queryAll(selectors: string, el?: HTMLElement): NodeListOf; 71 | 72 | export function remove(el: Element): void; 73 | 74 | export function assignStyle(element: E, ...styleSources: object[]): E; -------------------------------------------------------------------------------- /karma.conf.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const browsers = (process.env.TEST_BROWSERS || 'ChromeHeadless').split(','); 4 | 5 | // use puppeteer provided Chrome for testing 6 | process.env.CHROME_BIN = require('puppeteer').executablePath(); 7 | 8 | module.exports = function(karma) { 9 | karma.set({ 10 | 11 | frameworks: [ 12 | 'webpack', 13 | 'mocha', 14 | 'chai' 15 | ], 16 | 17 | files: [ 18 | 'test/*.js' 19 | ], 20 | 21 | preprocessors: { 22 | 'test/*.js': [ 'webpack' ] 23 | }, 24 | 25 | reporters: [ 'progress' ], 26 | 27 | browsers, 28 | 29 | singleRun: true, 30 | autoWatch: false, 31 | 32 | webpack: { 33 | mode: 'development' 34 | } 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /lib/attr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set attribute `name` to `val`, or get attr `name`. 3 | * 4 | * @param {Element} el 5 | * @param {String} name 6 | * @param {String} [val] 7 | * @api public 8 | */ 9 | export default function attr(el, name, val) { 10 | 11 | // get 12 | if (arguments.length == 2) { 13 | return el.getAttribute(name); 14 | } 15 | 16 | // remove 17 | if (val === null) { 18 | return el.removeAttribute(name); 19 | } 20 | 21 | // set 22 | el.setAttribute(name, val); 23 | 24 | return el; 25 | } -------------------------------------------------------------------------------- /lib/classes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Taken from https://github.com/component/classes 3 | * 4 | * Without the component bits. 5 | */ 6 | 7 | /** 8 | * toString reference. 9 | */ 10 | 11 | const toString = Object.prototype.toString; 12 | 13 | /** 14 | * Wrap `el` in a `ClassList`. 15 | * 16 | * @param {Element} el 17 | * @return {ClassList} 18 | * @api public 19 | */ 20 | 21 | export default function classes(el) { 22 | return new ClassList(el); 23 | } 24 | 25 | /** 26 | * Initialize a new ClassList for `el`. 27 | * 28 | * @param {Element} el 29 | * @api private 30 | */ 31 | 32 | function ClassList(el) { 33 | if (!el || !el.nodeType) { 34 | throw new Error('A DOM element reference is required'); 35 | } 36 | this.el = el; 37 | this.list = el.classList; 38 | } 39 | 40 | /** 41 | * Add class `name` if not already present. 42 | * 43 | * @param {String} name 44 | * @return {ClassList} 45 | * @api public 46 | */ 47 | 48 | ClassList.prototype.add = function(name) { 49 | this.list.add(name); 50 | return this; 51 | }; 52 | 53 | /** 54 | * Remove class `name` when present, or 55 | * pass a regular expression to remove 56 | * any which match. 57 | * 58 | * @param {String|RegExp} name 59 | * @return {ClassList} 60 | * @api public 61 | */ 62 | 63 | ClassList.prototype.remove = function(name) { 64 | if ('[object RegExp]' == toString.call(name)) { 65 | return this.removeMatching(name); 66 | } 67 | 68 | this.list.remove(name); 69 | return this; 70 | }; 71 | 72 | /** 73 | * Remove all classes matching `re`. 74 | * 75 | * @param {RegExp} re 76 | * @return {ClassList} 77 | * @api private 78 | */ 79 | 80 | ClassList.prototype.removeMatching = function(re) { 81 | const arr = this.array(); 82 | for (let i = 0; i < arr.length; i++) { 83 | if (re.test(arr[i])) { 84 | this.remove(arr[i]); 85 | } 86 | } 87 | return this; 88 | }; 89 | 90 | /** 91 | * Toggle class `name`, can force state via `force`. 92 | * 93 | * For browsers that support classList, but do not support `force` yet, 94 | * the mistake will be detected and corrected. 95 | * 96 | * @param {String} name 97 | * @param {Boolean} force 98 | * @return {ClassList} 99 | * @api public 100 | */ 101 | 102 | ClassList.prototype.toggle = function(name, force) { 103 | if ('undefined' !== typeof force) { 104 | if (force !== this.list.toggle(name, force)) { 105 | this.list.toggle(name); // toggle again to correct 106 | } 107 | } else { 108 | this.list.toggle(name); 109 | } 110 | return this; 111 | }; 112 | 113 | /** 114 | * Return an array of classes. 115 | * 116 | * @return {Array} 117 | * @api public 118 | */ 119 | 120 | ClassList.prototype.array = function() { 121 | return Array.from(this.list); 122 | }; 123 | 124 | /** 125 | * Check if class `name` is present. 126 | * 127 | * @param {String} name 128 | * @return {ClassList} 129 | * @api public 130 | */ 131 | 132 | ClassList.prototype.has = 133 | ClassList.prototype.contains = function(name) { 134 | return this.list.contains(name); 135 | }; 136 | -------------------------------------------------------------------------------- /lib/clear.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Clear utility 3 | */ 4 | 5 | /** 6 | * Removes all children from the given element 7 | * 8 | * @param {Element} element 9 | * 10 | * @return {Element} the element (for chaining) 11 | */ 12 | export default function clear(element) { 13 | var child; 14 | 15 | while ((child = element.firstChild)) { 16 | element.removeChild(child); 17 | } 18 | 19 | return element; 20 | } -------------------------------------------------------------------------------- /lib/closest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Closest 3 | * 4 | * @param {Element} el 5 | * @param {string} selector 6 | * @param {boolean} checkYourSelf (optional) 7 | */ 8 | export default function(element, selector, checkYourSelf) { 9 | var actualElement = checkYourSelf ? element : element.parentNode; 10 | 11 | return actualElement && typeof actualElement.closest === 'function' && actualElement.closest(selector) || null; 12 | } 13 | -------------------------------------------------------------------------------- /lib/delegate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | import closest from './closest'; 6 | import event from './event'; 7 | 8 | /** 9 | * Delegate event `type` to `selector` 10 | * and invoke `fn(e)`. A callback function 11 | * is returned which may be passed to `.unbind()`. 12 | * 13 | * @param {Element} el 14 | * @param {String} selector 15 | * @param {String} type 16 | * @param {Function} fn 17 | * @param {Boolean} capture 18 | * @return {Function} 19 | * @api public 20 | */ 21 | 22 | // Some events don't bubble, so we want to bind to the capture phase instead 23 | // when delegating. 24 | var forceCaptureEvents = [ 'focus', 'blur' ]; 25 | 26 | function bind(el, selector, type, fn, capture) { 27 | if (forceCaptureEvents.indexOf(type) !== -1) { 28 | capture = true; 29 | } 30 | 31 | return event.bind(el, type, function(e) { 32 | var target = e.target || e.srcElement; 33 | e.delegateTarget = closest(target, selector, true, el); 34 | if (e.delegateTarget) { 35 | fn.call(el, e); 36 | } 37 | }, capture); 38 | } 39 | 40 | /** 41 | * Unbind event `type`'s callback `fn`. 42 | * 43 | * @param {Element} el 44 | * @param {String} type 45 | * @param {Function} fn 46 | * @param {Boolean} capture 47 | * @api public 48 | */ 49 | function unbind(el, type, fn, capture) { 50 | if (forceCaptureEvents.indexOf(type) !== -1) { 51 | capture = true; 52 | } 53 | 54 | return event.unbind(el, type, fn, capture); 55 | } 56 | 57 | export default { 58 | bind, 59 | unbind 60 | }; -------------------------------------------------------------------------------- /lib/domify.js: -------------------------------------------------------------------------------- 1 | export { 2 | default 3 | } from 'domify'; -------------------------------------------------------------------------------- /lib/event.js: -------------------------------------------------------------------------------- 1 | import * as event from 'component-event'; 2 | 3 | export default event; 4 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | export { assign as assignStyle } from './style.js'; 2 | export { default as attr } from './attr.js'; 3 | export { default as classes } from './classes.js'; 4 | export { default as clear } from './clear.js'; 5 | export { default as closest } from './closest.js'; 6 | export { default as delegate } from './delegate.js'; 7 | export { default as domify } from './domify.js'; 8 | export { default as event } from './event.js'; 9 | export { default as matches } from './matches.js'; 10 | export { 11 | default as query, 12 | all as queryAll 13 | } from './query.js'; 14 | export { default as remove } from './remove.js'; 15 | -------------------------------------------------------------------------------- /lib/matches.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param { HTMLElement } element 3 | * @param { String } selector 4 | * 5 | * @return { boolean } 6 | */ 7 | export default function matches(element, selector) { 8 | return element && typeof element.matches === 'function' && element.matches(selector) || false; 9 | } -------------------------------------------------------------------------------- /lib/query.js: -------------------------------------------------------------------------------- 1 | export default function query(selector, el) { 2 | el = el || document; 3 | 4 | return el.querySelector(selector); 5 | } 6 | 7 | export function all(selector, el) { 8 | el = el || document; 9 | 10 | return el.querySelectorAll(selector); 11 | } -------------------------------------------------------------------------------- /lib/remove.js: -------------------------------------------------------------------------------- 1 | export default function remove(el) { 2 | el.parentNode && el.parentNode.removeChild(el); 3 | } -------------------------------------------------------------------------------- /lib/style.js: -------------------------------------------------------------------------------- 1 | import { forEach } from 'min-dash'; 2 | 3 | /** 4 | * Assigns style attributes in a style-src compliant way. 5 | * 6 | * @param {Element} element 7 | * @param {...Object} styleSources 8 | * 9 | * @return {Element} the element 10 | */ 11 | export function assign(element, ...styleSources) { 12 | const target = element.style; 13 | 14 | forEach(styleSources, function(style) { 15 | if (!style) { 16 | return; 17 | } 18 | 19 | forEach(style, function(value, key) { 20 | target[key] = value; 21 | }); 22 | }); 23 | 24 | return element; 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "min-dom", 3 | "version": "5.1.1", 4 | "description": "A minimal dom utility toolbelt", 5 | "type": "module", 6 | "exports": { 7 | ".": { 8 | "import": "./dist/index.js", 9 | "types": "./dist/index.d.ts" 10 | }, 11 | "./package.json": "./package.json" 12 | }, 13 | "types": "./dist/index.d.ts", 14 | "scripts": { 15 | "all": "run-s lint test check-types", 16 | "pretest": "run-s bundle", 17 | "bundle": "rollup -c --bundleConfigAsCjs", 18 | "lint": "eslint . --ext js,cjs", 19 | "prepare": "run-s bundle", 20 | "test": "karma start karma.conf.cjs", 21 | "check-types": "tsc --noEmit" 22 | }, 23 | "keywords": [ 24 | "dom", 25 | "util", 26 | "utility", 27 | "minimal", 28 | "event", 29 | "query", 30 | "jquery", 31 | "component" 32 | ], 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/bpmn-io/min-dom" 36 | }, 37 | "author": "bpmn.io", 38 | "license": "MIT", 39 | "sideEffects": false, 40 | "dependencies": { 41 | "domify": "^2.0.0", 42 | "min-dash": "^4.2.1" 43 | }, 44 | "devDependencies": { 45 | "@rollup/plugin-commonjs": "^25.0.7", 46 | "@rollup/plugin-node-resolve": "^15.2.3", 47 | "@types/chai": "^4.3.12", 48 | "@types/mocha": "^10.0.6", 49 | "chai": "^4.4.1", 50 | "component-event": "^0.2.1", 51 | "eslint": "^8.57.0", 52 | "eslint-plugin-bpmn-io": "^1.0.0", 53 | "karma": "^6.4.3", 54 | "karma-chai": "^0.1.0", 55 | "karma-chrome-launcher": "^3.2.0", 56 | "karma-firefox-launcher": "^2.1.3", 57 | "karma-mocha": "^2.0.1", 58 | "karma-webpack": "^5.0.0", 59 | "mocha": "^10.3.0", 60 | "npm-run-all": "^4.1.2", 61 | "puppeteer": "^22.4.0", 62 | "rollup": "^4.12.1", 63 | "typescript": "^5.3.3", 64 | "webpack": "^5.90.3" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | 4 | import pkg from './package.json'; 5 | 6 | const pkgExport = pkg.exports['.']; 7 | 8 | function pgl(plugins = []) { 9 | return [ 10 | nodeResolve({ 11 | resolveOnly: [ 'component-event' ] 12 | }), 13 | commonjs(), 14 | ...plugins 15 | ]; 16 | } 17 | 18 | export default [ 19 | { 20 | input: 'lib/index.js', 21 | output: [ 22 | { file: pkgExport.import, format: 'es', sourcemap: true } 23 | ], 24 | plugins: pgl() 25 | } 26 | ]; -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plugin:bpmn-io/mocha" 3 | } -------------------------------------------------------------------------------- /test/attr.js: -------------------------------------------------------------------------------- 1 | import { 2 | attr, 3 | domify 4 | } from 'min-dom'; 5 | 6 | 7 | describe('attr', function() { 8 | 9 | it('should get attribute', function() { 10 | 11 | var node = domify('
'); 12 | 13 | expect(attr(node, 'title')).to.eql('FOO'); 14 | }); 15 | 16 | 17 | it('should set attribute', function() { 18 | 19 | var node = domify('
'); 20 | 21 | // when 22 | attr(node, 'title', 'BAR'); 23 | 24 | expect(attr(node, 'title')).to.eql('BAR'); 25 | }); 26 | 27 | }); -------------------------------------------------------------------------------- /test/bind.js: -------------------------------------------------------------------------------- 1 | import { 2 | event 3 | } from 'min-dom'; 4 | 5 | 6 | describe('event', function() { 7 | 8 | it('should expose bind', function() { 9 | expect(typeof event.bind).to.equal('function'); 10 | }); 11 | 12 | 13 | it('should expose unbind', function() { 14 | expect(typeof event.unbind).to.equal('function'); 15 | }); 16 | 17 | }); -------------------------------------------------------------------------------- /test/classes.js: -------------------------------------------------------------------------------- 1 | import { 2 | classes, 3 | domify 4 | } from 'min-dom'; 5 | 6 | 7 | describe('classes', function() { 8 | 9 | it('should query class', function() { 10 | 11 | var node = domify('
'); 12 | 13 | expect(classes(node).has('foo')).to.be.true; 14 | }); 15 | 16 | 17 | it('should toggle attribute', function() { 18 | 19 | var node = domify('
'); 20 | 21 | // when 22 | classes(node).toggle('foo'); 23 | 24 | expect(classes(node).has('foo')).to.be.false; 25 | }); 26 | 27 | }); -------------------------------------------------------------------------------- /test/clear.js: -------------------------------------------------------------------------------- 1 | import { 2 | clear, 3 | domify 4 | } from 'min-dom'; 5 | 6 | 7 | describe('clear', function() { 8 | 9 | it('should remove children', function() { 10 | 11 | var node = domify(` 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
` 27 | ); 28 | 29 | // when 30 | var cleared = clear(node); 31 | 32 | expect(cleared).to.eql(node); 33 | expect(cleared.childNodes.length).to.eql(0); 34 | }); 35 | 36 | }); -------------------------------------------------------------------------------- /test/closest.js: -------------------------------------------------------------------------------- 1 | import { 2 | closest, 3 | domify, 4 | query 5 | } from 'min-dom'; 6 | 7 | 8 | describe('closest', function() { 9 | 10 | it('should get closest parent', function() { 11 | 12 | // given 13 | var node = domify('
' + 14 | '
' + 15 | '
' + 16 | '
' + 17 | '
'); 18 | 19 | var child = query('.child', node); 20 | 21 | // then 22 | expect(closest(child, '.root')).to.equal(node); 23 | expect(closest(node, '.root', true)).to.equal(node); 24 | }); 25 | 26 | 27 | it('should work with document fragments', function() { 28 | 29 | // given 30 | var node = domify('
' + 31 | '
' + 32 | '
' + 33 | '
' + 34 | '
'); 35 | 36 | var child = query('.child', node); 37 | 38 | var fragment = document.createDocumentFragment(); 39 | fragment.appendChild(node); 40 | 41 | // then 42 | expect(closest(child, '.root')).to.equal(node); 43 | expect(closest(node, '.root', true)).to.equal(node); 44 | expect(closest(node, '.outside')).to.be.null; 45 | }); 46 | 47 | 48 | it('should work with shadow dom', function() { 49 | 50 | // .root 51 | // | -> (shadow - This is not a HTML document nor HTML element) 52 | // | -> .shadowRoot 53 | // | -> .shadowChild 54 | 55 | // given 56 | var root = domify('
'); 57 | var shadow = root.attachShadow({ mode: 'open' }); 58 | 59 | var shadowRoot = domify('
' + 60 | '
' + 61 | '
'); 62 | 63 | shadow.appendChild(shadowRoot); 64 | 65 | var shadowChild = query('.shadowChild', shadowRoot); 66 | 67 | // then 68 | expect(closest(shadowChild, '.shadowRoot')).to.equal(shadowRoot); 69 | expect(closest(shadowRoot, '.shadowRoot', true)).to.equal(shadowRoot); 70 | expect(closest(shadowChild, '.outside')).to.be.null; 71 | expect(closest(shadowChild, '.root')).to.be.null; 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /test/delegate.js: -------------------------------------------------------------------------------- 1 | import { 2 | delegate 3 | } from 'min-dom'; 4 | 5 | 6 | describe('delegate', function() { 7 | 8 | it('should expose bind', function() { 9 | expect(typeof delegate.bind).to.equal('function'); 10 | }); 11 | 12 | 13 | it('should expose unbind', function() { 14 | expect(typeof delegate.unbind).to.equal('function'); 15 | }); 16 | 17 | }); -------------------------------------------------------------------------------- /test/integration/ts.spec.ts: -------------------------------------------------------------------------------- 1 | import * as MinDom from 'min-dom'; 2 | 3 | import { expect } from 'chai'; 4 | 5 | 6 | describe('typed', function() { 7 | 8 | it('should expose utilities', function() { 9 | expect(MinDom.assignStyle).to.exist; 10 | expect(MinDom.attr).to.exist; 11 | expect(MinDom.classes).to.exist; 12 | expect(MinDom.clear).to.exist; 13 | expect(MinDom.closest).to.exist; 14 | expect(MinDom.delegate).to.exist; 15 | expect(MinDom.domify).to.exist; 16 | expect(MinDom.event).to.exist; 17 | expect(MinDom.matches).to.exist; 18 | expect(MinDom.query).to.exist; 19 | expect(MinDom.queryAll).to.exist; 20 | expect(MinDom.remove).to.exist; 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /test/matches.js: -------------------------------------------------------------------------------- 1 | import { 2 | domify, 3 | matches 4 | } from 'min-dom'; 5 | 6 | 7 | describe('matches', function() { 8 | 9 | it('should match element by selector', function() { 10 | 11 | var node = domify('
'); 12 | 13 | // when 14 | expect(matches(node, '.root')).to.be.true; 15 | expect(matches(node, 'bar')).to.be.false; 16 | }); 17 | 18 | 19 | it('should match custom element', function() { 20 | 21 | var node = domify(''); 22 | 23 | // then 24 | expect(matches(node, 'foo-bar')).to.be.true; 25 | }); 26 | 27 | 28 | it('should handle missing element', function() { 29 | 30 | expect(matches(null, 'foo-bar')).to.be.false; 31 | }); 32 | 33 | }); -------------------------------------------------------------------------------- /test/query.js: -------------------------------------------------------------------------------- 1 | import { 2 | domify, 3 | query, 4 | queryAll 5 | } from 'min-dom'; 6 | 7 | 8 | describe('query', function() { 9 | 10 | it('should get single', function() { 11 | 12 | var node = domify('
' + 13 | '
' + 14 | '
' + 15 | '
' + 16 | '
' + 17 | '
'); 18 | 19 | // then 20 | expect(query('.child', node)).to.exist; 21 | expect(query('.child:first-child', node)).to.exist; 22 | 23 | expect(query('foo', node)).not.to.exist; 24 | }); 25 | 26 | 27 | it('should get list', function() { 28 | 29 | var node = domify('
' + 30 | '
' + 31 | '
' + 32 | '
' + 33 | '
' + 34 | '
'); 35 | 36 | // then 37 | expect(queryAll('.child', node)).to.have.length(2); 38 | expect(queryAll('.child:first-child', node)).to.have.length(1); 39 | 40 | expect(queryAll('foo', node)).to.have.length(0); 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /test/remove.js: -------------------------------------------------------------------------------- 1 | import { 2 | domify, 3 | remove 4 | } from 'min-dom'; 5 | 6 | 7 | describe('remove', function() { 8 | 9 | it('should remove element', function() { 10 | 11 | var node = domify('
'); 12 | var child = node.childNodes[0]; 13 | 14 | // when 15 | remove(child); 16 | 17 | expect(node.childNodes.length).to.eql(0); 18 | expect(child.parentNode).not.to.exist; 19 | }); 20 | 21 | }); -------------------------------------------------------------------------------- /test/style.js: -------------------------------------------------------------------------------- 1 | import { 2 | domify, 3 | assignStyle 4 | } from 'min-dom'; 5 | 6 | 7 | describe('style', function() { 8 | 9 | it('should assign styles', function() { 10 | 11 | // given 12 | var node = domify('
'); 13 | 14 | var style1 = { 15 | height: '1px', 16 | zIndex: '1' 17 | }; 18 | 19 | var style2 = { 20 | zIndex: '2' 21 | }; 22 | 23 | // when 24 | assignStyle(node, style1, style2); 25 | 26 | // then 27 | var appliedStyle = node.style; 28 | expect(appliedStyle).to.exist; 29 | expect(appliedStyle.width).to.eql('0px'); 30 | expect(appliedStyle.height).to.eql('1px'); 31 | expect(appliedStyle.zIndex).to.eql('2'); 32 | }); 33 | 34 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ES2018", 5 | "module": "Node16", 6 | "moduleResolution": "NodeNext" 7 | }, 8 | "include": [ 9 | "lib", 10 | "test" 11 | ] 12 | } --------------------------------------------------------------------------------