npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

element-detector

v1.0.0

Published

Detect elements appearing in the DOM using MutationObserver

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 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

npm install element-detector

For Userscripts

Add the library via @require in your userscript metadata:

// ==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

import {detect} from 'element-detector';

// Called for each new element matching the selector
detect('.notification', (element) => {
  console.log('New notification:', element);
});

Without callback (Promise)

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<T> (optional)

  • Configuration object

Returns

  • Detector - when callback is provided
  • Promise<T> - 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
detect('.item', callback, {existing: true});

once: boolean (default: false)

  • When true, stops watching after the first matching element
  • Automatically calls stop() on the detector
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
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
detect('.widget', callback, {timeout: 5000});

signal: AbortSignal

  • External AbortSignal for manual control
  • When the signal is aborted, watching stops
const controller = new AbortController();
detect('.element', callback, {signal: controller.signal});

// Later
controller.abort();

TypeScript Generics

Specify element type for proper typing:

detect<HTMLButtonElement>('.submit', (button) => {
  button.disabled = false;
});

detect<HTMLAnchorElement>('a', (link) => {
  console.log(link.href);
});

const img = await detect<HTMLImageElement>('img.hero');

Interfaces

interface DetectOptions<T extends Element = Element> {
  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

detect('.date-picker', (element) => {
  new DatePicker(element);
}, {existing: true});

Waiting for dynamic content

const item = await detect('.product[data-id="12345"]', {
  timeout: 10000
});

item.scrollIntoView();

Modal dialogs

detect('.modal.confirmation', (modal) => {
  const confirmBtn = modal.querySelector('.confirm');
  confirmBtn?.addEventListener('click', handleConfirm);
});

Processing external links

detect('a', (link) => {
  link.setAttribute('target', '_blank');
  link.setAttribute('rel', 'noopener noreferrer');
}, {
  filter: (link) => link.hostname !== window.location.hostname,
  existing: true
});

Timeout with fallback

const detector = detect('.slow-widget', (widget) => {
  initialize(widget);
}, {timeout: 5000});

detector.signal.addEventListener('abort', () => {
  showFallback();
});

Manual control with AbortController

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

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

const widget = await detect('.third-party-widget', {
  timeout: 5000,
  existing: true
});

initializeIntegration(widget);

Browser Support

Check browser compatibility

Requires: