{"id":16117919,"url":"https://github.com/wessberg/connection-observer","last_synced_at":"2025-03-17T18:30:40.230Z","repository":{"id":66337068,"uuid":"170409974","full_name":"wessberg/connection-observer","owner":"wessberg","description":"An API that provides a way to asynchronously observe the connectedness of a target Node inside a document","archived":false,"fork":false,"pushed_at":"2024-03-28T15:00:35.000Z","size":620,"stargazers_count":14,"open_issues_count":8,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-11T07:46:19.489Z","etag":null,"topics":["asynchronous","connectedcallback","connectedness","connection","disconnected","disconnectedcallback","dom","mutationobserver","observer"],"latest_commit_sha":null,"homepage":null,"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/wessberg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","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},"funding":{"github":"wessberg","patreon":"wessberg"}},"created_at":"2019-02-12T23:56:39.000Z","updated_at":"2025-02-07T04:46:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"1d08c6cc-bbb7-4484-a9e9-6fb9458b26cc","html_url":"https://github.com/wessberg/connection-observer","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wessberg%2Fconnection-observer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wessberg%2Fconnection-observer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wessberg%2Fconnection-observer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wessberg%2Fconnection-observer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wessberg","download_url":"https://codeload.github.com/wessberg/connection-observer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243878439,"owners_count":20362432,"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":["asynchronous","connectedcallback","connectedness","connection","disconnected","disconnectedcallback","dom","mutationobserver","observer"],"created_at":"2024-10-09T20:47:45.942Z","updated_at":"2025-03-17T18:30:39.893Z","avatar_url":"https://github.com/wessberg.png","language":"TypeScript","funding_links":["https://github.com/sponsors/wessberg","https://patreon.com/wessberg","https://www.patreon.com/bePatron?u=11315442"],"categories":[],"sub_categories":[],"readme":"\u003c!-- SHADOW_SECTION_LOGO_START --\u003e\n\n\u003cdiv\u003e\u003cimg alt=\"Logo\" src=\"https://raw.githubusercontent.com/wessberg/connection-observer/master/documentation/asset/logo.png\" height=\"110\"   /\u003e\u003c/div\u003e\n\n\u003c!-- SHADOW_SECTION_LOGO_END --\u003e\n\n\u003c!-- SHADOW_SECTION_DESCRIPTION_SHORT_START --\u003e\n\n\u003e An API that provides a way to asynchronously observe the connectedness of a target Node or querySelector inside a document\n\n\u003c!-- SHADOW_SECTION_DESCRIPTION_SHORT_END --\u003e\n\n\u003c!-- SHADOW_SECTION_BADGES_START --\u003e\n\n\u003ca href=\"https://npmcharts.com/compare/%40wessberg%2Fconnection-observer?minimal=true\"\u003e\u003cimg alt=\"Downloads per month\" src=\"https://img.shields.io/npm/dm/%40wessberg%2Fconnection-observer.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/%40wessberg%2Fconnection-observer\"\u003e\u003cimg alt=\"NPM version\" src=\"https://badge.fury.io/js/%40wessberg%2Fconnection-observer.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://david-dm.org/wessberg/connection-observer\"\u003e\u003cimg alt=\"Dependencies\" src=\"https://img.shields.io/david/wessberg%2Fconnection-observer.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/wessberg/connection-observer/graphs/contributors\"\u003e\u003cimg alt=\"Contributors\" src=\"https://img.shields.io/github/contributors/wessberg%2Fconnection-observer.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/prettier/prettier\"\u003e\u003cimg alt=\"code style: prettier\" src=\"https://img.shields.io/badge/code_style-prettier-ff69b4.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-yellow.svg\"    /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.patreon.com/bePatron?u=11315442\"\u003e\u003cimg alt=\"Support on Patreon\" src=\"https://img.shields.io/badge/patreon-donate-green.svg\"    /\u003e\u003c/a\u003e\n\n\u003c!-- SHADOW_SECTION_BADGES_END --\u003e\n\n\u003c!-- SHADOW_SECTION_DESCRIPTION_LONG_START --\u003e\n\n## Description\n\n\u003c!-- SHADOW_SECTION_DESCRIPTION_LONG_END --\u003e\n\n`ConnectionObserver` is a tiny (1kb) API that provides a way to asynchronously observe the connectedness of a target Node or querySelector inside a document.\n\nWith `ConnectionObserver`, you have a low-level building block that can be used to build functionality on top of when you need to\nperform work when a Node lives inside the DOM, and/or perform work when it becomes detached.\n\n\u003c!-- SHADOW_SECTION_FEATURES_START --\u003e\n\n### Features\n\n\u003c!-- SHADOW_SECTION_FEATURES_END --\u003e\n\n- Familiar API: Follows the same conventions as [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API), [`PerformanceObserver`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver), and [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)\n- Asynchronous: Entries are batched together as microtasks\n- Tiny (1kb)\n- Performant\n\n\u003c!-- SHADOW_SECTION_FEATURE_IMAGE_START --\u003e\n\n\u003c!-- SHADOW_SECTION_FEATURE_IMAGE_END --\u003e\n\n\u003c!-- SHADOW_SECTION_TOC_START --\u003e\n\n## Table of Contents\n\n- [Description](#description)\n  - [Features](#features)\n- [Table of Contents](#table-of-contents)\n- [Install](#install)\n  - [npm](#npm)\n  - [Yarn](#yarn)\n  - [pnpm](#pnpm)\n- [Usage](#usage)\n  - [Constructing a ConnectionObserver](#constructing-a-connectionobserver)\n  - [Observing Nodes for connectedness](#observing-nodes-for-connectedness)\n  - [Observing querySelectors for connectedness](#observing-queryselectors-for-connectedness)\n  - [Disconnecting the ConnectionObserver](#disconnecting-the-connectionobserver)\n  - [Taking ConnectionRecords immediately](#taking-connectionrecords-immediately)\n- [API reference](#api-reference)\n  - [ConnectionObserver](#connectionobserver)\n  - [ConnectionCallback](#connectioncallback)\n  - [ConnectionRecord](#connectionrecord)\n- [Contributing](#contributing)\n- [Maintainers](#maintainers)\n- [Backers](#backers)\n  - [Patreon](#patreon)\n- [FAQ](#faq)\n  - [Why can't you just use MutationObservers for this](#why-cant-you-just-use-mutationobservers-for-this)\n  - [Why wouldn't you use MutationEvents for this](#why-wouldnt-you-use-mutationevents-for-this)\n- [License](#license)\n\n\u003c!-- SHADOW_SECTION_TOC_END --\u003e\n\n\u003c!-- SHADOW_SECTION_INSTALL_START --\u003e\n\n## Install\n\n### npm\n\n```\n$ npm install @wessberg/connection-observer\n```\n\n### Yarn\n\n```\n$ yarn add @wessberg/connection-observer\n```\n\n### pnpm\n\n```\n$ pnpm add @wessberg/connection-observer\n```\n\n\u003c!-- SHADOW_SECTION_INSTALL_END --\u003e\n\n\u003c!-- SHADOW_SECTION_USAGE_START --\u003e\n\n## Usage\n\n\u003c!-- SHADOW_SECTION_USAGE_END --\u003e\n\nIf you are familiar with the family of observers such as `MutationObserver` and `IntersectionObserver`, you will feel right at home\nwith `ConnectionObserver`. Not only is the API very similar, it is also asynchronous and batches together records on the microtask queue.\n\n```typescript\nimport {ConnectionObserver} from \"@wessberg/connection-observer\";\n\n// Hook up a new ConnectionObserver\nconst observer = new ConnectionObserver(entries =\u003e {\n\t// For each entry, print the connection state as well as the target node to the console\n\tfor (const {connected, target} of entries) {\n\t\tconsole.log(\"target:\", target);\n\t\tconsole.log(\"connected:\", connected);\n\t}\n});\n\n// Observe 'someElement' for connectedness\nobserver.observe(someElement);\n\n// Eventually disconnect the observer when you are done observing elements for connectedness\nobserver.disconnect();\n```\n\n### Constructing a ConnectionObserver\n\nThe `ConnectionObserver` constructor creates and returns a new observer which invokes a specified callback when there are new connectedness entries available.\nIf you don't call provide any Nodes to the `observe` method on the `ConnectionObserver` instance, the callback will never be called since no Nodes will be observed for connectedness.\n\n```typescript\nconst connectionObserver = new ConnectionObserver(callback);\n```\n\n### Observing Nodes for connectedness\n\nThe `ConnectionObserver` method `observe` configures the `ConnectionObserver` callback to begin receiving notifications of changes to the connectedness of the given Node(s).\nThe callback will be invoked immediately with the connectedness of the observed Node(s).\n\n```typescript\nconnectionObserver.observe(target);\n```\n\n### Observing querySelectors for connectedness\n\nThe `ConnectionObserver` method `observe` also accepts a query selector as the first argument, instead of a specific Node. This enables you to subscribe to connectedness events\nfor any Nodes that matches your querySelector inside of the document, including any Shadow roots. You can use this functionality for performing actions on elements matching your\nquerySelector as they enter and leave the DOM. For example:\n\n```typescript\nconst connectionObserver = new ConnectionObserver(entries =\u003e {\n\tfor (const {connected, target} of entries) {\n\t\tif (connected) {\n\t\t\tmakeImageFancy(target);\n\t\t}\n\t}\n});\nconnectionObserver.observe(`img[data-fancy]`);\n```\n\n### Disconnecting the ConnectionObserver\n\nThe `ConnectionObserver` method `disconnect` will stop watching for the connectedness of all observed Nodes such that the callback won't be triggered any longer.\n\n```typescript\nconnectionObserver.disconnect();\n```\n\n### Taking ConnectionRecords immediately\n\n`ConnectionObserver` is asynchronous which means that `ConnectionEntries` will be batched together and be provided to the callback given in the constructor (see [this section](#constructing-a-connectionobserver)) as a microtask.\nThe method `takeRecords` returns the entries that are currently queued in the batch and haven't been processed yet, leaving the connection queue empty. This may be useful if you want to immediately fetch all pending connection records immediately before disconnecting the observer, so that any pending changes can be processed.\n\n```typescript\nconst entries = connectionObserver.takeRecords();\n```\n\n## API reference\n\nThis section includes a more code-oriented introduction to the types and interfaces of `ConnectionObserver`\n\n### ConnectionObserver\n\n```typescript\nclass ConnectionObserver {\n\t[Symbol.toStringTag]: string;\n\n\t/**\n\t * Constructs a new ConnectionObserver\n\t * @param {ConnectionCallback} callback\n\t */\n\tconstructor(callback: ConnectionCallback);\n\n\t/**\n\t * Observe the given node or query selector for connections/disconnections.\n\t * If given a Node, that specific Node will be observed. If given a query selector, such\n\t * as for example \"img[data-some-attr]\", for each new MutationRecord, the query selector\n\t * will be executed and the matched nodes will be observed for connections/disconnections\n\t * @param {string} target\n\t * @example {observe(\"img[data-some-attr]\")}\n\t */\n\tobserve(target: Node | string): void;\n\n\t/**\n\t * Takes the records immediately (instead of waiting for the next flush)\n\t * @return {ConnectionRecord[]}\n\t */\n\ttakeRecords(): ConnectionRecord[];\n\n\t/**\n\t * Disconnects the ConnectionObserver such that none of its callbacks will be invoked any longer\n\t */\n\tdisconnect(): void;\n}\n```\n\n### ConnectionCallback\n\nA `ConnectionCallback` must be provided to the constructor of [`ConnectionObserver`](#connectionobserver) and will be invoked when\nthere are new [ConnectionRecords](#connectionrecord) available.\n\n```typescript\ntype ConnectionCallback = (entries: ConnectionRecord[], observer: IConnectionObserver) =\u003e void;\n```\n\n### ConnectionRecord\n\n[ConnectionCallbacks](#connectioncallback) are invoked with an array of `ConnectionRecord`s. Those have the following members:\n\n```typescript\ninterface ConnectionRecord {\n\t/**\n\t * Whether or not the node is Connected\n\t */\n\treadonly connected: boolean;\n\n\t/**\n\t * The target Node\n\t */\n\treadonly target: Node;\n}\n```\n\n\u003c!-- SHADOW_SECTION_CONTRIBUTING_START --\u003e\n\n## Contributing\n\nDo you want to contribute? Awesome! Please follow [these recommendations](./CONTRIBUTING.md).\n\n\u003c!-- SHADOW_SECTION_CONTRIBUTING_END --\u003e\n\n\u003c!-- SHADOW_SECTION_MAINTAINERS_START --\u003e\n\n## Maintainers\n\n| \u003ca href=\"mailto:frederikwessberg@hotmail.com\"\u003e\u003cimg alt=\"Frederik Wessberg\" src=\"https://avatars2.githubusercontent.com/u/20454213?s=460\u0026v=4\" height=\"70\"   /\u003e\u003c/a\u003e                                                                |\n| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Frederik Wessberg](mailto:frederikwessberg@hotmail.com)\u003cbr\u003e\u003cstrong\u003eTwitter\u003c/strong\u003e: [@FredWessberg](https://twitter.com/FredWessberg)\u003cbr\u003e\u003cstrong\u003eGithub\u003c/strong\u003e: [@wessberg](https://github.com/wessberg)\u003cbr\u003e_Lead Developer_ |\n\n\u003c!-- SHADOW_SECTION_MAINTAINERS_END --\u003e\n\n\u003c!-- SHADOW_SECTION_BACKERS_START --\u003e\n\n## Backers\n\n| \u003ca href=\"https://usebubbles.com\"\u003e\u003cimg alt=\"Bubbles\" src=\"https://uploads-ssl.webflow.com/5d682047c28b217055606673/5e5360be16879c1d0dca6514_icon-thin-128x128%402x.png\" height=\"70\"   /\u003e\u003c/a\u003e | \u003ca href=\"https://github.com/cblanc\"\u003e\u003cimg alt=\"Christopher Blanchard\" src=\"https://avatars0.githubusercontent.com/u/2160685?s=400\u0026v=4\" height=\"70\"   /\u003e\u003c/a\u003e |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Bubbles](https://usebubbles.com)\u003cbr\u003e\u003cstrong\u003eTwitter\u003c/strong\u003e: [@use_bubbles](https://twitter.com/use_bubbles)                                                                              | [Christopher Blanchard](https://github.com/cblanc)                                                                                                         |\n\n### Patreon\n\n\u003ca href=\"https://www.patreon.com/bePatron?u=11315442\"\u003e\u003cimg alt=\"Patrons on Patreon\" src=\"https://img.shields.io/endpoint.svg?url=https://shieldsio-patreon.herokuapp.com/wessberg\"  width=\"200\"  /\u003e\u003c/a\u003e\n\n\u003c!-- SHADOW_SECTION_BACKERS_END --\u003e\n\n\u003c!-- SHADOW_SECTION_FAQ_START --\u003e\n\n## FAQ\n\n\u003c!-- SHADOW_SECTION_FAQ_END --\u003e\n\n#### Why can't you just use MutationObservers for this\n\nWith [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), we can watch for changes being made to the DOM tree from any root,\nbut using it to watch for when an arbitrary Node is attached to or detached from the DOM is very hard since that requires tracking all [Shadow Roots](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot).\n\nThere is [an ongoing discussion](https://github.com/whatwg/dom/issues/533) about adding support for tracking connectedness of any Node via MutationObservers, and this library aims to render itself obsolete if and when\nthat becomes a reality in favor of a polyfill.\n\n#### Why wouldn't you use MutationEvents for this\n\n[MutationEvents](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events) are deprecated and I would discourage you from using them.\nAdditionally, these were designed and implemented in browser before Shadow DOM v1 came to be, and they are somewhat unreliable for tracking the connectedness of Nodes inside of Shadow roots.\nAdditionally, they are synchronous which is bad for performance and has proven to be performance-killers in numerous benchmarks and investigations.\n\n\u003c!-- SHADOW_SECTION_LICENSE_START --\u003e\n\n## License\n\nMIT © [Frederik Wessberg](mailto:frederikwessberg@hotmail.com) ([@FredWessberg](https://twitter.com/FredWessberg)) ([Website](https://github.com/wessberg))\n\n\u003c!-- SHADOW_SECTION_LICENSE_END --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwessberg%2Fconnection-observer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwessberg%2Fconnection-observer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwessberg%2Fconnection-observer/lists"}