https://github.com/febalist/element-detector
https://github.com/febalist/element-detector
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/febalist/element-detector
- Owner: febalist
- License: isc
- Created: 2023-03-20T14:35:06.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2025-10-26T22:26:34.000Z (8 months ago)
- Last Synced: 2025-10-26T22:27:23.549Z (8 months ago)
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/element-detector
- Size: 190 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# element-detector
A library for detecting elements appearing in the DOM. Executes callbacks when elements matching a selector are added to the page.
Uses [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to efficiently track DOM changes and notify about new elements. Works at any stage of page lifecycle - during initial load, after DOMContentLoaded, or during dynamic content updates.
## Installation
### For Bundlers
```bash
npm install element-detector
```
### For Userscripts
Add the library via `@require` in your userscript metadata:
```javascript
// ==UserScript==
// @name My Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Example userscript using element-detector
// @require https://cdn.jsdelivr.net/npm/element-detector/dist/index.global.min.js
// @grant none
// ==/UserScript==
(function () {
'use strict';
// Library is available as window.ElementDetector
const {detect} = window.ElementDetector;
// Now you can use detect() in your script
detect('.some-element', (element) => {
console.log('Element found:', element);
});
})();
```
Alternative CDN links:
- jsDelivr (minified): `https://cdn.jsdelivr.net/npm/element-detector/dist/index.global.min.js`
- jsDelivr (unminified): `https://cdn.jsdelivr.net/npm/element-detector/dist/index.global.js`
- unpkg (minified): `https://unpkg.com/element-detector/dist/index.global.min.js`
- unpkg (unminified): `https://unpkg.com/element-detector/dist/index.global.js`
## Usage
### With callback
```typescript
import {detect} from 'element-detector';
// Called for each new element matching the selector
detect('.notification', (element) => {
console.log('New notification:', element);
});
```
### Without callback (Promise)
```typescript
import {detect} from 'element-detector';
// Returns a promise that resolves with the first matching element
const modal = await detect('.modal');
console.log('Modal appeared:', modal);
```
## API
### `detect(selector, callback?, options?)`
#### Parameters
**selector**: `string`
- CSS selector to match elements
**callback**: `(element: T) => void` (optional)
- Function called for each matching element
- If omitted, returns a Promise
**options**: `DetectOptions` (optional)
- Configuration object
#### Returns
- `Detector` - when callback is provided
- `Promise` - when callback is omitted (defaults to `{ once: true }`)
### Options
**existing**: `boolean` (default: `false`)
- When `true`, processes elements that already exist in the DOM at the time `detect` is called
- When `false`, only processes elements added after the call
```typescript
detect('.item', callback, {existing: true});
```
**once**: `boolean` (default: `false`)
- When `true`, stops watching after the first matching element
- Automatically calls `stop()` on the detector
```typescript
detect('.dialog', callback, {once: true});
```
**filter**: `(element: T) => boolean`
- Additional filtering function applied to matched elements
- Only elements for which the function returns `true` will trigger the callback
```typescript
detect('a', callback, {
filter: (link) => link.hostname !== window.location.hostname
});
```
**timeout**: `number`
- Time in milliseconds after which watching automatically stops
- Creates an internal AbortController that triggers after the specified time
```typescript
detect('.widget', callback, {timeout: 5000});
```
**signal**: `AbortSignal`
- External AbortSignal for manual control
- When the signal is aborted, watching stops
```typescript
const controller = new AbortController();
detect('.element', callback, {signal: controller.signal});
// Later
controller.abort();
```
### TypeScript Generics
Specify element type for proper typing:
```typescript
detect('.submit', (button) => {
button.disabled = false;
});
detect('a', (link) => {
console.log(link.href);
});
const img = await detect('img.hero');
```
### Interfaces
```typescript
interface DetectOptions {
existing?: boolean;
filter?: (element: T) => boolean;
once?: boolean;
timeout?: number;
signal?: AbortSignal;
}
interface Detector {
signal: AbortSignal; // Fires when watching stops
stop: () => void; // Manually stop watching
}
```
## Examples
### Initializing widgets
```typescript
detect('.date-picker', (element) => {
new DatePicker(element);
}, {existing: true});
```
### Waiting for dynamic content
```typescript
const item = await detect('.product[data-id="12345"]', {
timeout: 10000
});
item.scrollIntoView();
```
### Modal dialogs
```typescript
detect('.modal.confirmation', (modal) => {
const confirmBtn = modal.querySelector('.confirm');
confirmBtn?.addEventListener('click', handleConfirm);
});
```
### Processing external links
```typescript
detect('a', (link) => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
}, {
filter: (link) => link.hostname !== window.location.hostname,
existing: true
});
```
### Timeout with fallback
```typescript
const detector = detect('.slow-widget', (widget) => {
initialize(widget);
}, {timeout: 5000});
detector.signal.addEventListener('abort', () => {
showFallback();
});
```
### Manual control with AbortController
```typescript
const controller = new AbortController();
detect('.live-update', (update) => {
processUpdate(update);
}, {signal: controller.signal});
// Stop watching when user leaves page section
document.addEventListener('navigate', () => {
controller.abort();
});
```
### Combining timeout and signal
```typescript
const controller = new AbortController();
const detector = detect('.element', callback, {
signal: controller.signal,
timeout: 10000
});
// Stops when either timeout is reached OR controller.abort() is called
// detector.signal combines both signals
```
### Waiting for third-party scripts
```typescript
const widget = await detect('.third-party-widget', {
timeout: 5000,
existing: true
});
initializeIntegration(widget);
```
## Browser Support
[Check browser compatibility](https://caniuse.com/mutationobserver,abortcontroller,mdn-api_abortsignal_any_static,promises)
Requires:
- [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
- [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
- [AbortSignal.any()](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static)
- [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)