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

symbiotic

v2.1.4

Published

A lightweight DOM attachment framework

Downloads

23

Readme

Symbiote

A lightweight, high-performance not-a-framework JavaScript library that makes it easy to attach javascript to your DOM.

🚀 Features

  • 🎯 Automatic DOM Attachment: Attach behaviors to elements using CSS selectors
  • ⚡ High Performance: Sub-millisecond event handling, 0ms initialization
  • 🔄 Dynamic DOM Support: Automatically handles added/removed elements
  • 🎨 Template Rendering: Built-in template system with content rendering
  • 🌐 Global Behaviors: Define reusable behaviors across your application
  • 📦 Batching System: Queue DOM operations for optimal performance
  • 🧠 Memory Efficient: Zero memory leaks, automatic cleanup
  • 🎭 CSS Selector Support: Classes, IDs, attributes, complex selectors
  • 🔧 Error Handling: Graceful degradation, comprehensive error logging
  • 📱 Browser Compatible: Works in all modern browsers
  • 📏 Lightweight: ~2KB minified, zero dependencies

📦 Installation

npm install symbiotic

🎯 Quick Start

Basic Usage

import createSymbiote from 'symbiotic';

const symbiote = createSymbiote({
  '.js-button': (el) => {
    el.addEventListener('click', () => console.log('Button clicked!'));
  },
  '.js-modal': (el) => {
    el.addEventListener('click', () => el.style.display = 'none');
  }
});

// Automatically waits for DOM and attaches behaviors
await symbiote.attach();

HTML Structure

<button class="js-button">Click me!</button>
<div class="js-modal">Modal content</div>

🎨 CSS Selector Support

Symbiote supports comprehensive CSS selectors:

const symbiote = createSymbiote({
  // Class selectors
  '.js-button': (el) => { /* button behavior */ },
  
  // ID selectors
  '#main-content': (el) => { /* main content behavior */ },
  
  // Tag selectors
  'button': (el) => { /* all buttons */ },
  
  // Attribute selectors
  '[data-action="save"]': (el) => { /* save buttons */ },
  '[disabled]': (el) => { /* disabled elements */ },
  
  // Complex selectors
  '.header > .close-btn': (el) => { /* close buttons in header */ },
  '.parent .child': (el) => { /* child elements */ },
  
  // Universal selector
  '*': (el) => { /* all elements */ }
});

🔄 Template Rendering System

Symbiote includes a powerful template rendering system:

HTML Template

<template class="js-user-card">
  <div class="card">
    <h3>John Doe</h3>
    <p>[email protected]</p>
    <button class="js-edit">Edit</button>
  </div>
</template>

JavaScript Setup

const symbiote = createSymbiote({
  '.js-user-card': (renderFn, batch) => {
    // renderFn renders the template element's content after the template
    // and passes setup functions for the newly rendered elements
    renderFn((el) => {
      // This setup function runs on each newly rendered element
      // el is the rendered element (not the template)
      el.style.border = '1px solid #ccc';
      el.addEventListener('click', () => {
        console.log('User card clicked!');
      });
    }); // Uses template.innerHTML as content source
  },
  
  '.js-edit': (el) => {
    el.addEventListener('click', () => {
      console.log('Edit button clicked!');
    });
  }
});

How Template Rendering Works

  1. Template Element: Contains the content to be rendered
  2. renderFn(setupFunction): Renders the template's content after the template element
  3. Content Source: Uses template.content
  4. Setup Function: The function passed to renderFn runs on each newly rendered element
  5. Element Parameter: The setup function receives the rendered element (not the template)

🌐 Global Behaviors

Define reusable behaviors that work across your entire application:

import { defineSetup } from 'symbiotic';

// Define global behaviors
defineSetup('.js-tooltip', (el) => {
  el.addEventListener('mouseenter', () => showTooltip(el));
  el.addEventListener('mouseleave', () => hideTooltip(el));
});

defineSetup('[data-confirm]', (el) => {
  el.addEventListener('click', (e) => {
    if (!confirm(el.dataset.confirm)) {
      e.preventDefault();
    }
  });
});

// All existing and future Symbiote instances will use these behaviors
const symbiote = createSymbiote({
  '.js-button': (el) => {
    // This button will also get tooltip behavior if it has js-tooltip class
  }
});

📦 Batching System

Queue DOM operations for optimal performance:

const symbiote = createSymbiote({
  '.js-batch': (el, batch) => {
    // batch queues DOM operations for optimal performance
    batch(() => {
      el.style.color = 'red';
      el.style.fontSize = '18px';
      el.textContent = 'Updated!';
    });
    
    // Returns a Promise that resolves when operations complete
    batch(() => {
      // More DOM operations
    }).then(() => {
      console.log('Batch operations completed!');
    });
  }
});

🔧 Advanced Usage

Cleanup Functions

Return cleanup functions to handle element removal:

const symbiote = createSymbiote({
  '.js-component': (el) => {
    const handler = () => console.log('Component clicked');
    el.addEventListener('click', handler);
    
    // Return cleanup function
    return () => {
      el.removeEventListener('click', handler);
    };
  }
});

Custom Root Element

Attach to a specific part of the DOM:

const symbiote = createSymbiote({
  '.js-widget': (el) => { /* widget behavior */ }
});

// Only attach to elements within #app
await symbiote.attach(document.getElementById('app'));

Update Method

Check for new setup functions on existing elements:

const symbiote = createSymbiote({
  '.js-button': (el) => { /* button behavior */ }
});

await symbiote.attach();

// Later, define new behaviors
defineSetup('.js-new-feature', (el) => { /* new behavior */ });

// Update existing elements to get new behaviors
await symbiote.update();

📊 Performance

Symbiote is designed for high performance:

  • Initialization: 0ms (instantaneous)
  • DOM Attachment: 0ms for 50 elements
  • Event Handling: 0.2-0.3ms per event
  • Memory Usage: Zero memory leaks
  • Stress Test: 100 events in 32ms

Browser Performance Test

Run the included browser performance test:

npm run test:browser

This opens an interactive test suite that validates performance in real browser environments.

🧪 Testing

Symbiote includes comprehensive testing:

# Run all tests
npm test

# Run tests once
npm run test:run

# Run with coverage
npm run test:coverage

# Browser performance test
npm run test:browser

📚 API Reference

createSymbiote(setupFunctions)

Creates a new Symbiote instance.

Parameters:

  • setupFunctions (Object): Object mapping CSS selectors to setup functions

Returns:

  • Symbiote: A Symbiote instance

symbiote.attach(root?)

Attaches behaviors to the DOM.

Parameters:

  • root (HTMLElement, optional): Root element to attach to. Defaults to document.body

Returns:

  • Promise<void>: Resolves when attachment is complete

symbiote.update(root?)

Updates existing elements with new setup functions.

Parameters:

  • root (HTMLElement, optional): Root element to update. Defaults to document.body

Returns:

  • Promise<void>: Resolves when update is complete

defineSetup(selector, setupFunction)

Creates a global setup function.

Parameters:

  • selector (string): CSS selector for the setup function
  • setupFunction (function): Function to execute when elements match

Returns:

  • Object: Object with remove() method to unregister the setup function

🎯 Examples

Counter Component

const symbiote = createSymbiote({
  '.js-counter': (el) => {
    let count = 0;
    el.addEventListener('click', () => {
      count++;
      el.textContent = `Clicked ${count} times`;
    });
  }
});

await symbiote.attach();

Modal System

const symbiote = createSymbiote({
  '.js-modal-trigger': (el) => {
    el.addEventListener('click', () => {
      const modal = document.querySelector('.js-modal');
      modal.style.display = 'block';
    });
  },
  '.js-modal-close': (el) => {
    el.addEventListener('click', () => {
      const modal = el.closest('.js-modal');
      modal.style.display = 'none';
    });
  }
});

await symbiote.attach();

Form Validation

const symbiote = createSymbiote({
  '.js-validate': (el) => {
    el.addEventListener('blur', () => {
      if (!el.value) {
        el.classList.add('error');
      } else {
        el.classList.remove('error');
      }
    });
  }
});

await symbiote.attach();

Data Table

const symbiote = createSymbiote({
  '.js-sort': (el) => {
    el.addEventListener('click', () => {
      const column = el.dataset.column;
      sortTable(column);
    });
  },
  '.js-filter': (el) => {
    el.addEventListener('input', () => {
      const value = el.value;
      filterTable(value);
    });
  }
});

await symbiote.attach();

🌍 Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

📦 Build Outputs

Symbiote provides multiple build formats:

  • ES Modules: dist/symbiote.esm.js
  • UMD: dist/symbiote.umd.js
  • IIFE: dist/symbiote.iife.js
  • Minified versions: All formats include .min.js versions

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

📄 License

MIT


Symbiote - Making HTML smart, one selector at a time! 🚀