nodeRegistry

Low-level DOM node registry for reactive effects and delegated events with automatic cleanup.

API

interface NodeRegistry {
  nodes: Map<Node, NodeRegistryItem>;
  get(node: Node): NodeRegistryItem;
  addEffect(node: Node, effectFn: () => void): void;
  addEvent(node: Node, type: string, handler: EventListener): void;
  clean(node: Node): void;
  observer: MutationObserver;
}

interface NodeRegistryItem {
  effects?: Set<() => void>;
  events?: Map<string, EventListener>;
}

nodes

Direct access to the internal mapping of DOM nodes to their registry entries.

import { nodeRegistry } from '@hellajs/dom';

const element = document.createElement('div');
nodeRegistry.addEffect(element, () => {});

// Access the internal nodes map
console.log('Total registered nodes:', nodeRegistry.nodes.size);

// Inspect specific node entry
const entry = nodeRegistry.nodes.get(element);
if (entry) {
  console.log('Effects:', entry.effects?.size || 0);
  console.log('Events:', entry.events?.size || 0);
}

get

Retrieves or creates the registry entry for a DOM node.

import { nodeRegistry } from '@hellajs/dom';

const element = document.createElement('div');

// Get or create registry entry
const registry = nodeRegistry.get(element);
console.log('Effects count:', registry.effects?.size || 0);
console.log('Events count:', registry.events?.size || 0);

// Entry is created if it doesn't exist
const newElement = document.createElement('span');
const newRegistry = nodeRegistry.get(newElement);
console.log('New entry created:', newRegistry);

addEffect

Registers a reactive effect for a DOM node.

import { nodeRegistry } from '@hellajs/dom';
import { signal } from '@hellajs/core';

const element = document.createElement('div');
const count = signal(0);

// Register a reactive effect
nodeRegistry.addEffect(element, () => {
  element.textContent = `Count: ${count()}`;
});

// Effect runs automatically when count changes
count(5); // Updates element content

// Multiple effects can be registered
nodeRegistry.addEffect(element, () => {
  element.style.color = count() > 10 ? 'red' : 'black';
});

addEvent

Registers an event handler for a DOM node.

import { nodeRegistry } from '@hellajs/dom';

const button = document.createElement('button');

// Register click handler
nodeRegistry.addEvent(button, 'click', (event) => {
  console.log('Button clicked!', event.target);
});

// Register multiple event types
nodeRegistry.addEvent(button, 'mouseover', () => {
  console.log('Mouse over button');
});

nodeRegistry.addEvent(button, 'mouseout', () => {
  console.log('Mouse left button');
});

document.body.appendChild(button);

clean

Manually disposes effects and clears events for a node.

import { nodeRegistry } from '@hellajs/dom';
import { signal } from '@hellajs/core';

const element = document.createElement('div');
const count = signal(0);

// Register effect and event
nodeRegistry.addEffect(element, () => {
  element.textContent = `Count: ${count()}`;
});
nodeRegistry.addEvent(element, 'click', () => console.log('Clicked'));

// Manual cleanup (rarely needed)
nodeRegistry.clean(element);

// Node is cleaned up - no more reactive updates
count(10); // Element content won't update

observer

The global MutationObserver instance that monitors DOM changes.

import { nodeRegistry } from '@hellajs/dom';

// Access the global observer
console.log('Observer:', nodeRegistry.observer);

// Check if observer is connected
const isObserving = nodeRegistry.observer !== null;
console.log('Observing DOM changes:', isObserving);

// The observer automatically handles cleanup when nodes are removed
const element = document.createElement('div');
nodeRegistry.addEffect(element, () => {});
document.body.appendChild(element);
element.remove(); // Observer detects removal and cleans up

Key Concepts

Automatic Cleanup

The registry automatically cleans up nodes when they’re removed from the document via a global MutationObserver.

import { nodeRegistry } from '@hellajs/dom';

const element = document.createElement('div');

// Register effects and events
nodeRegistry.addEffect(element, () => { /* ... */ });
nodeRegistry.addEvent(element, 'click', () => { /* ... */ });

// Cleanup happens automatically when removed
document.body.appendChild(element);
element.remove(); // Effects disposed in next microtask

Effect Registration

Reactive effects are stored per node and automatically disposed when the node is removed.

import { nodeRegistry } from '@hellajs/dom';
import { signal } from '@hellajs/core';

const element = document.createElement('div');
const count = signal(0);

nodeRegistry.addEffect(element, () => {
  element.textContent = `Count: ${count()}`;
});

Event Handler Management

Event handlers are registered for the global delegation system and cleaned up automatically.

import { nodeRegistry } from '@hellajs/dom';

const button = document.createElement('button');

nodeRegistry.addEvent(button, 'click', (event) => {
  console.log('Button clicked!', event);
});

Registry Inspection

Access registry data for debugging and advanced use cases.

import { nodeRegistry } from '@hellajs/dom';

const element = document.createElement('div');

// Get registry entry
const registry = nodeRegistry.get(element);
console.log('Effects count:', registry.effects?.size || 0);
console.log('Events count:', registry.events?.size || 0);

// Direct access to all registered nodes
console.log('Total registered nodes:', nodeRegistry.nodes.size);

Important Considerations

Internal API

This is a low-level API primarily used internally by HellaJS. Most applications should use higher-level APIs like mount and forEach.

Memory Management

Manual cleanup is rarely needed since a MutationObserver handles removal detection.

// ✅ Automatic cleanup (recommended)
element.remove(); // Cleanup happens automatically

// ⚠️ Manual cleanup (rarely needed)
nodeRegistry.clean(element);

Observer Coordination

The global MutationObserver watches document.body with { childList: true, subtree: true } to detect all node removals. Cleanup runs in microtasks to batch multiple changes efficiently.