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 🙏

© 2026 – Pkg Stats / Ryan Hefner

xenonjs

v1.0.6

Published

Lightweight event-driven DOM utility library with modular architecture

Downloads

701

Readme

XenonJS

A tiny, event-driven DOM toolkit for modern JavaScript — built for clean chaining, smart event handling, and zero overhead.

No framework. No dependencies. Just fast, expressive DOM control. DOM manipulation, event handling, and more — without the overhead of a full framework.

Why XenonJS?

  • Zero dependencies — pure JavaScript, no bloat
  • Tiny footprint — ships as a single ES module (~3KB gzipped)
  • Chainable API — fluent interface for clean, readable code
  • Smart event system — built-in deduplication, delegation, and scoped cleanup via an internal event bucket
  • Static methods — use X.hide('.ad') for quick one-liners without chaining
  • Built-in context menus — custom right-click menus out of the box
  • Modern — ES2020+, built with Vite

Why not just use vanilla JS?

Because XenonJS gives you:

  • Cleaner chaining
X('.card').addClass('active').css('opacity', '1')

vs

document.querySelectorAll('.card').forEach(el => {
  el.classList.add('active')
  el.style.opacity = '1'
})

Installation

npm

npm install xenonjs

CDN / <script> tag (unpkg)

Drop in a single <script> tag — no bundler required. The library is exposed on window.XenonJS and the default export (X) is available as window.XenonJS.default.

<!-- Core only (no bus) -->
<script src="https://unpkg.com/xenonjs/dist/index.iife.js"></script>
<script>
  const X = XenonJS.use;
  X('.card').addClass('active').css('opacity', '1');
  X.hide('.banner');
</script>

<!-- core + bus (X and X.bus both available) -->
<script src="https://unpkg.com/xenonjs/dist/all.iife.js"></script>
<script>
  const X = XenonJS.use;
  X('.card').addClass('active');
  X.bus.on('user:login', (data) => console.log(data));
  X.bus.emit('user:login', { name: 'Alice' });
</script>

ES module in the browser (no bundler)

<!-- Core only -->
<script type="module">
  import X from 'https://unpkg.com/xenonjs/dist/index.js';
  X('.card').addClass('active');
</script>

<!-- Core + bus separately -->
<script type="module">
  import X from 'https://unpkg.com/xenonjs/dist/index.js';
  import 'https://unpkg.com/xenonjs/dist/bus.js'; // attaches X.bus
  X.bus.on('ready', () => console.log('ready'));
</script>

<!-- Core + bus in one import -->
<script type="module">
  import X from 'https://unpkg.com/xenonjs/dist/all.js';
  X.bus.on('ready', () => console.log('ready'));
</script>

ES module with a bundler (Vite, webpack, Rollup…)

import X from 'xenonjs';          // core
import 'xenonjs/bus';             // attach X.bus
import X from 'xenonjs/all';      // core + bus in one import

Quick Start

import X from 'xenonjs';

// Select and manipulate
X('.card').addClass('active').css('opacity', '1');

// Event handling with delegation
X('.list').on('click', '.item', (e, matched) => {
    console.log('Clicked:', matched.textContent);
});

// One-liner static methods
X.hide('.banner');
X.text('.title', 'Hello, Xenon!');

Smart Event System (no duplicates, no leaks)

XenonJS automatically prevents duplicate listeners and tracks them internally.

X('.btn').on('click', handler)
X('.btn').on('click', handler) // ← ignored, no duplicate

You also get:

  • Delegation built-in
  • Easy cleanup with .off()
  • Memory-safe via WeakMap

Built-in Context Menus (no plugin needed)

Create fully custom right-click menus in seconds:

X('.canvas').context([
  { text: 'Copy', action: copy },
  { text: 'Paste', action: paste }
])
  • Menus auto-position to stay within the viewport.
  • Close on outside click, Esc, window blur, or resize.
  • Supports svg, icon (image URL), and action (function or string stored as data-context-action).

Example: Interactive List

X('.list')
  .on('click', '.item', (e, el) => {
    X(el).toggleClass('active')
  })

X('.add-btn').on('click', () => {
  X('#list').append('<li class="item">New item</li>')
})

API Reference

Selection & Traversal

| Method | Description | |--------|-------------| | X(selector) | Select elements by CSS selector, Element, NodeList, or array | | .i(index) | Get a wrapped element at a specific index | | .first() | Get the first matched element (wrapped) | | .last() | Get the last matched element (wrapped) | | .get(index?) | Get the raw DOM element (default: index 0) | | .length() | Number of matched elements | | .each(fn) | Iterate over matched elements | | .find(selector) | Find descendants matching a selector | | .parent() | Get parent elements | | .closest(selector) | Get nearest ancestor matching a selector |

const items = X('.list-item');

items.first().addClass('highlight');
items.last().text('Final item');
items.i(2).css('color', 'red');

X('.child').closest('.container').addClass('found');

DOM Manipulation

| Method | Description | |--------|-------------| | .append(content) | Insert content at the end of each element | | .prepend(content) | Insert content at the start of each element | | .before(content) | Insert content before each element | | .after(content) | Insert content after each element | | .empty() | Remove all child nodes | | .remove() | Remove matched elements from the DOM |

Content can be an HTML string or a DOM Element.

X('#list').append('<li>New item</li>');
X('.target').before('<hr>').after('<hr>');
X('.old-content').empty();
X('.disposable').remove();

Attributes, Data & Properties

| Method | Description | |--------|-------------| | .html(value?) | Get/set innerHTML | | .text(value?) | Get/set textContent | | .val(value?) | Get/set form input value | | .attr(name, value?) | Get/set an attribute | | .removeAttr(name) | Remove an attribute | | .data(name, value?) | Get/set a data attribute | | .removeData(name) | Remove a data attribute | | .prop(name, value?) | Get/set a DOM property | | .removeProp(name) | Remove a DOM property | | .css(prop, value?) | Get/set inline styles (string or object) |

// Getter (no value argument)
const title = X('.heading').text();
const href  = X('a.link').attr('href');

// Setter (returns wrapper for chaining)
X('.heading').text('Updated!');
X('.box').css({ background: '#222', color: '#fff', padding: '1rem' });
X('.card').data('id', '42');

Classes

| Method | Description | |--------|-------------| | .addClass(names) | Add one or more classes | | .removeClass(names) | Remove one or more classes | | .toggleClass(names, force?) | Toggle classes; optional boolean to force add/remove | | .hasClass(names) | Check if the first element has all given classes | | .is(selector) | Check if the first element matches a CSS selector |

Class names can be a space-separated string or an array.

X('.item').addClass('active highlighted');
X('.item').toggleClass('open');
X('.item').hasClass('active'); // true / false

Visibility

| Method | Description | |--------|-------------| | .hide() | Set display: none | | .show(display?) | Restore display (default: '') | | .toggle(force?) | Toggle visibility; optional boolean to force state |

X('.modal').show();
X('.tooltip').toggle();
X('.ad').hide();

State Helpers

| Method | Description | |--------|-------------| | .checked(value?) | Get/set the checked state of checkboxes/radios | | .disabled(value?) | Get/set the disabled state of inputs/buttons |

X('#agree').checked(true);
X('.submit-btn').disabled(false);

Events

XenonJS uses an internal event bucket system built on a WeakMap. This prevents duplicate handler registration and enables precise cleanup — all without leaking memory.

| Method | Description | |--------|-------------| | .on(event, [selector], callback, [options]) | Attach an event listener; optional delegation via selector | | .once(event, [selector], callback, [options]) | Like .on(), but fires only once | | .off(event?, [selector], [callback], [options]) | Remove specific or all listeners | | .onclick(callback) | Shorthand for .on('click', ...) | | .onchange(callback) | Shorthand for .on('change', ...) | | .trigger(eventName, detail?) | Dispatch a CustomEvent on matched elements |

// Basic event
X('.btn').on('click', (e, el) => console.log('Clicked!', el));

// Delegated event
X('.list').on('click', '.list-item', (e, matched) => {
    matched.classList.add('selected');
});

// One-time event
X('.welcome').once('click', () => alert('Hello!'));

// Cleanup
X('.btn').off('click');   // remove all click handlers
X('.btn').off();          // remove all handlers on element

// Custom events
X('.widget').on('data-loaded', (e) => console.log(e.detail));
X('.widget').trigger('data-loaded', { rows: 100 });

Context Menus

Create custom right-click menus declaratively.

X('.canvas').context([
    { text: 'Cut',    action: () => cut(),   svg: cutIcon },
    { text: 'Copy',   action: () => copy(),  svg: copyIcon },
    { text: 'Paste',  action: () => paste(), svg: pasteIcon },
], {
    id: 'editor-menu',
    className: 'dark-theme',
});
  • Menus auto-position to stay within the viewport.
  • Close on outside click, Esc, window blur, or resize.
  • Supports svg, icon (image URL), and action (function or string stored as data-context-action).

Static Methods

Every instance method is also available as a static method where the first argument is the selector:

X.addClass('.nav', 'open');
X.hide('.banner');
X.text('.title', 'Hello');
X.on('.btn', 'click', () => { /* ... */ });
X.toggle('.sidebar');

Bus Plugin — Decoupled Messaging

XenonJS ships with an optional event bus plugin for pub/sub communication between decoupled parts of your app.

import 'xenonjs/bus'; // attaches X.bus automatically

// Subscribe
const unsub = X.bus.on('user:login', (data) => console.log(data.name));

// Publish (async pipeline — each listener transforms the value)
const result = await X.bus.emit('cart:total', 100);

// One-time listener
X.bus.once('app:ready', (cfg) => init(cfg));

// Cleanup
unsub();                     // single listener
X.bus.off('user:login');     // all listeners for event
X.bus.off();                 // full reset

Supports wildcards, async pipelines, has() checks, and convenient unsubscribe handles.
See the full API and examples in docs/bus.README.md.

Project Structure

xenonjs/
├── src/
│   ├── index.js              # Core Wrapper class, X() factory, static API
│   ├── bus.js                # Event bus plugin (optional)
│   └── services/
│       ├── context.js         # Context menu system
│       └── util.js            # Shared helpers (class normalization, event keys)
├── index.html                 # Dev playground
├── dist/                      # Built outputs (ES, IIFE, UMD for each entry)
├── vite.config.js             # Vite build config (ES, IIFE, UMD library output)
├── package.json
└── examples.md                # Extended usage examples

Development

# Install dependencies
npm install

# Start dev server with hot reload
npm run dev

# Build library to dist/
npm run build

The build produces three formats for each entry point (index, bus, all):

| File | Format | Use case | |------|--------|----------| | dist/index.js | ES module | Bundlers, <script type="module"> | | dist/index.iife.js | IIFE | <script> tag, unpkg CDN | | dist/index.umd.js | UMD | CommonJS / AMD environments |

License

MIT © Stelios Ignatiadis