{"id":25936175,"url":"https://github.com/untemps/dom-observer","last_synced_at":"2026-05-01T21:05:36.246Z","repository":{"id":45080222,"uuid":"338604302","full_name":"untemps/dom-observer","owner":"untemps","description":"Class to observe when a specific element is added to or removed from the DOM","archived":false,"fork":false,"pushed_at":"2026-04-28T17:27:23.000Z","size":403,"stargazers_count":4,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-28T18:33:30.019Z","etag":null,"topics":["dom-elements","dom-mutations","dom-observer","mutationobserver","mutations-observer"],"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/untemps.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-02-13T15:26:25.000Z","updated_at":"2026-04-28T17:27:28.000Z","dependencies_parsed_at":"2024-06-20T22:09:19.600Z","dependency_job_id":null,"html_url":"https://github.com/untemps/dom-observer","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/untemps/dom-observer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untemps%2Fdom-observer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untemps%2Fdom-observer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untemps%2Fdom-observer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untemps%2Fdom-observer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/untemps","download_url":"https://codeload.github.com/untemps/dom-observer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untemps%2Fdom-observer/sbom","scorecard":{"id":911017,"data":{"date":"2025-08-11","repo":{"name":"github.com/untemps/dom-observer","commit":"42a3436b2bfd7e726156266407b99ae3d6205e35"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/12 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/index.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/index.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/untemps/dom-observer/index.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/index.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/untemps/dom-observer/index.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/index.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/untemps/dom-observer/index.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"62 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-wv8q-r932-8hc7","Warn: Project is vulnerable to: GHSA-8266-84wp-wv5c","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-hxwm-x553-x359","Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-4r62-v4vq-hr96","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-px4h-xg32-q955","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-x2pg-mjhr-2m5x","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx","Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch","Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T19:11:55.889Z","repository_id":45080222,"created_at":"2025-08-24T19:11:55.889Z","updated_at":"2025-08-24T19:11:55.889Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32503226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"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":["dom-elements","dom-mutations","dom-observer","mutationobserver","mutations-observer"],"created_at":"2025-03-04T01:50:28.282Z","updated_at":"2026-05-01T21:05:36.234Z","avatar_url":"https://github.com/untemps.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @untemps/dom-observer\n\nClass to observe DOM mutations of a specific element in one-shot or continuous mode.\n\nThe class is a wrapper around the MutationObserver API to target an element in particular.  \nThat means you can observe an element to be added to the DOM and access to its properties, an attribute from that element to be changed and get the old and the new values, the element to be removed from the DOM and destroy all its dependencies.\n\n![npm](https://img.shields.io/npm/v/@untemps/dom-observer?style=for-the-badge)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/untemps/dom-observer/publish.yml?style=for-the-badge)](https://github.com/untemps/dom-observer/actions)\n![Codecov](https://img.shields.io/codecov/c/github/untemps/dom-observer?style=for-the-badge)\n\n## Installation\n\n```bash\nyarn add @untemps/dom-observer\n```\n\n## Usage\n\nImport `DOMObserver`:\n\n```javascript\nimport { DOMObserver } from '@untemps/dom-observer'\n```\n\nCreate an instance of `DOMObserver`:\n\n```javascript\nconst observer = new DOMObserver()\n```\n\n### Watch for recurring mutations\n\nUse the `watch` method when you want to be notified **every time** a mutation occurs — for instance, tracking all successive attribute changes on an element or reacting to every matching node added to the DOM.\n\n```javascript\nimport { DOMObserver } from '@untemps/dom-observer'\n\n// Track every attribute change on an element\nconst observer = new DOMObserver()\nobserver.watch('#foo', (node, event, { attributeName, oldValue } = {}) =\u003e {\n\tconsole.log(`${attributeName} changed from ${oldValue} to ${node.getAttribute(attributeName)}`)\n}, { events: [DOMObserver.CHANGE] })\n\n// React to every matching node added or removed\nconst listObserver = new DOMObserver()\nlistObserver.watch('.list-item', (node, event) =\u003e {\n\tif (event === DOMObserver.ADD) console.log(`Item added: ${node.textContent}`)\n\tif (event === DOMObserver.REMOVE) console.log(`Item removed: ${node.textContent}`)\n}, { events: [DOMObserver.ADD, DOMObserver.REMOVE] })\n```\n\nUnlike `wait`, `watch` does not return a Promise. It returns `this`, allowing method chaining. Call `clear()` to stop the observation.\n\nPass `once: true` to stop the observation automatically after the first matching event, without needing to call `clear()` manually:\n\n```javascript\nobserver.watch('#foo', (node, event) =\u003e {\n\tdoSomething(node)  // called exactly once\n}, { events: [DOMObserver.ADD], once: true })\n```\n\nPass `debounce` to delay the callback until mutations have stopped for a given number of milliseconds — useful when you only care about the final state after a burst of rapid changes:\n\n```javascript\nobserver.watch('#progress', (node) =\u003e {\n\tconsole.log('final value:', node.getAttribute('data-value'))\n}, {\n\tevents: [DOMObserver.CHANGE],\n\tattributeFilter: ['data-value'],\n\tdebounce: 100,\n})\n```\n\nPass a `timeout` to automatically stop the observation if no matching mutation occurs within the allotted time:\n\n```javascript\nconst observer = new DOMObserver()\nobserver.watch('#foo', (node, event) =\u003e {\n\tconsole.log(`Event: ${event}`)\n}, {\n\tevents: [DOMObserver.ADD],\n\ttimeout: 3000,\n\tonError: (err) =\u003e console.error(err.message),\n})\n```\n\n#### `watch` method arguments\n\n| Props               | Type              | Description                                                                                                                                              |\n| ------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `target`            | Element or String | DOM element or selector of the DOM element to observe. See [querySelector spec](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) |\n| `onEvent`           | Function          | Callback triggered each time an event occurs on the observed element                                                                                    |\n| `options`           | Object            | Options object:                                                                                                                                          |\n| - `events`          | Array             | List of [events](#events) to observe (All events are observed by default)                                                                                |\n| - `attributeFilter` | Array             | List of attribute names to observe (DOMObserver.CHANGE event only)                                                                                       |\n| - `timeout`         | Number            | Duration (in ms) after which observation stops if no matching mutation occurred. Triggers `onError` when elapsed. Must be `0` or a positive finite number — throws `[TIMEOUT]` otherwise. |\n| - `onError`         | Function          | Callback triggered when `timeout` elapses with no matching mutation                                                                                      |\n| - `signal`          | AbortSignal       | An `AbortSignal` to stop the observation. If already aborted, `watch()` returns immediately without observing.                                           |\n| - `once`            | Boolean           | When `true`, automatically calls `clear()` after the first matching event. Defaults to `false`.                                                          |\n| - `debounce`        | Number            | Milliseconds to wait after the last mutation before invoking the callback. The callback receives the last mutation's arguments. `0` disables debouncing. |\n| - `root`            | Element or String | DOM element or CSS selector to use as the observation root. Only mutations within this subtree are observed. Defaults to `document.documentElement`.     |\n| - `filter`          | Function          | `(node, event, options?) =\u003e boolean`. Called before invoking the callback. Return `false` to skip the event and keep observing.                           |\n\n#### `onEvent` callback arguments\n\n| Props             | Type           | Description                                                                 |\n| ----------------- | -------------- | --------------------------------------------------------------------------- |\n| `node`            | Element        | Observed element node                                                       |\n| `event`           | String         | Event that triggered the callback                                           |\n| `options`         | Object         | Present only for `CHANGE` events:                                           |\n| - `attributeName` | String         | Name of the attribute that changed                                          |\n| - `oldValue`      | String or null | Value of the attribute before the mutation                                  |\n\n#### `onError` callback arguments\n\n| Props   | Type  | Description  |\n| ------- | ----- | ------------ |\n| `error` | Error | Error thrown |\n\n### Wait for a one-shot mutation\n\nUse the `wait` method to get a Promise that resolves on the **first** matching mutation.\n\n```javascript\nimport { DOMObserver } from '@untemps/dom-observer'\n\nconst observer = new DOMObserver()\nconst { node, event, options: { attributeName } = {} } = await observer.wait('#foo', { events: [DOMObserver.REMOVE, DOMObserver.CHANGE] })\nswitch (event) {\n\tcase DOMObserver.REMOVE: {\n\t\tconsole.log('Element ' + node.id + ' has been removed')\n\t\tbreak\n\t}\n\tcase DOMObserver.CHANGE: {\n\t\tconsole.log('Element ' + node.id + ' has been changed (' + attributeName + ')')\n\t\tbreak\n\t}\n}\n```\n\nPass an **array of targets** to resolve as soon as any one of them fires a matching event. The resolved value includes a `target` field identifying which entry won:\n\n```javascript\nconst { node, target } = await observer.wait(['#success', '#error'], {\n\tevents: [DOMObserver.ADD],\n})\nconsole.log(`Matched: ${target}`)\n```\n\nOnce the first matching mutation occurs, the Promise resolves and the observation stops automatically. If a `timeout` is set and elapses before any matching mutation, the Promise rejects with a `[TIMEOUT]` error.\n\n#### `wait` method arguments\n\n| Props               | Type                       | Description                                                                                                                                              |\n| ------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `target`            | Element, String, or Array  | DOM element, selector, or array of either. When an array is passed, resolves on the first match across all entries.                                      |\n| `options`           | Object            | Options object:                                                                                                                                          |\n| - `events`          | Array             | List of [events](#events) to observe (All events are observed by default)                                                                                |\n| - `timeout`         | Number            | Duration (in ms) before rejecting the Promise with a `[TIMEOUT]` error. `0` disables the timeout. Must be `0` or a positive finite number — rejects with `[TIMEOUT]` otherwise.          |\n| - `attributeFilter` | Array             | List of attribute names to observe (DOMObserver.CHANGE event only)                                                                                       |\n| - `signal`          | AbortSignal       | An `AbortSignal` to cancel the observation. If already aborted, the Promise rejects immediately with an `AbortError`.                                    |\n| - `root`            | Element or String | DOM element or CSS selector to use as the observation root. Only mutations within this subtree are observed. Defaults to `document.documentElement`.     |\n| - `filter`          | Function          | `(node, event, options?) =\u003e boolean`. Called before resolving the Promise. Return `false` to skip the event and keep waiting.                             |\n\n#### Resolved value\n\n| Props             | Type                  | Description                                                                                          |\n| ----------------- | --------------------- | ---------------------------------------------------------------------------------------------------- |\n| `node`            | Element               | The matching DOM element                                                                             |\n| `event`           | String                | The event type that caused the Promise to settle                                                     |\n| `target`          | Element, String, or undefined | The entry from the targets array that matched. `undefined` when a single target was passed. |\n| `options`         | Object                | Present only for `CHANGE` events:                                                                    |\n| - `attributeName` | String                | Name of the attribute that changed                                                                   |\n| - `oldValue`      | String or null        | Value of the attribute before the mutation                                                           |\n\n#### Events\n\nDOMObserver static properties list all observable events.\n\n| Props                | Description                                                                               |\n|----------------------|-------------------------------------------------------------------------------------------|\n| `DOMObserver.EXIST`  | Observe whether the element is already present in the DOM at observation start            |\n| `DOMObserver.ADD`    | Observe when the element is added to the DOM                                              |\n| `DOMObserver.REMOVE` | Observe when the element is removed from the DOM                                          |\n| `DOMObserver.CHANGE` | Observe when an attribute has changed on the element                                      |\n| `DOMObserver.EVENTS` | Array of all four events                                                                  |\n\nOne or more events can be passed to the `events` option of `wait` or `watch`. By default, all events are observed.\n\n```javascript\n{ events: [DOMObserver.ADD, DOMObserver.REMOVE] }\n{ events: DOMObserver.EVENTS }\n```\n\n### Check observation state\n\nThe `isObserving` getter returns `true` when an observation is currently active:\n\n```javascript\nconst observer = new DOMObserver()\nobserver.watch('#foo', (node, event) =\u003e { /* ... */ })\nconsole.log(observer.isObserving) // true\nobserver.clear()\nconsole.log(observer.isObserving) // false\n```\n\n### Discard observation\n\nCall the `clear` method to discard observation. It returns `this`, allowing method chaining:\n\n```javascript\nobserver.clear()\n\n// Stop and immediately restart with a different target\nobserver.clear().watch('#bar', onEvent)\n```\n\n\u003e **Note:** Calling `wait()` or `watch()` on an instance that already has a pending `wait()` Promise will automatically reject that Promise with an `[ABORT]` error before starting the new observation. Handle this rejection if necessary:\n\u003e\n\u003e ```javascript\n\u003e import { DOMObserver, DOMObserverErrors } from '@untemps/dom-observer'\n\u003e\n\u003e const observer = new DOMObserver()\n\u003e observer.wait('#foo').catch((err) =\u003e {\n\u003e     if (err.message.startsWith(DOMObserverErrors.ABORT)) return // replaced by a new observation\n\u003e     throw err\n\u003e })\n\u003e observer.wait('#bar')  // previous promise is rejected with [ABORT]\n\u003e observer.watch('#baz', onEvent)  // also rejects a pending wait() with [ABORT]\n\u003e ```\n\n## Error constants\n\nThe library exports a `DOMObserverErrors` object and a `DOMObserverErrorCode` type for reliable error handling without fragile string matching:\n\n```typescript\nimport { DOMObserver, DOMObserverErrors } from '@untemps/dom-observer'\n\ntry {\n    await observer.wait('#foo', { timeout: 500 })\n} catch (e) {\n    const message = (e as Error).message\n    if (message.startsWith(DOMObserverErrors.TIMEOUT)) {\n        // handle timeout\n    } else if (message.startsWith(DOMObserverErrors.ABORT)) {\n        // replaced by another observation\n    }\n}\n```\n\n| Constant | Value | Thrown by |\n|---|---|---|\n| `DOMObserverErrors.TIMEOUT` | `'[TIMEOUT]'` | `wait()`, `watch()` when `timeout` elapses; also when `timeout` is an invalid value (`-1`, `NaN`, `Infinity`) |\n| `DOMObserverErrors.ABORT` | `'[ABORT]'` | `wait()` when replaced by a new call |\n| `DOMObserverErrors.EVENTS` | `'[EVENTS]'` | `wait()`, `watch()` when `events` array is empty |\n| `DOMObserverErrors.TARGET` | `'[TARGET]'` | `wait()`, `watch()` when `target` is an invalid CSS selector |\n\n## Example\n\n```javascript\nimport { DOMObserver } from '@untemps/dom-observer'\n\n// Continuous observation with timeout\nconst onError = (err) =\u003e console.error(err.message)\nconst observer = new DOMObserver()\nobserver.watch(\n    '.foo',\n    (node, event, { attributeName } = {}) =\u003e {\n        switch (event) {\n            case DOMObserver.EXIST: {\n                console.log('Element ' + node.id + ' exists already')\n                break\n            }\n            case DOMObserver.ADD: {\n                console.log('Element ' + node.id + ' has been added')\n                break\n            }\n            case DOMObserver.REMOVE: {\n                console.log('Element ' + node.id + ' has been removed')\n                break\n            }\n            case DOMObserver.CHANGE: {\n                console.log('Element ' + node.id + ' has been changed (' + attributeName + ')')\n                break\n            }\n        }\n    },\n    {\n        events: [DOMObserver.EXIST, DOMObserver.ADD, DOMObserver.REMOVE, DOMObserver.CHANGE],\n        timeout: 2000,\n        onError,\n        attributeFilter: ['class'],\n    }\n)\n```\n\n## Development\n\nA demo can be served for development purpose on `http://localhost:5173/` running:\n\n```\nyarn dev\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funtemps%2Fdom-observer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funtemps%2Fdom-observer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funtemps%2Fdom-observer/lists"}