made by alan w smith
source code socials other projects

bitty

signals based reactivity in a tiny web component
non-minified version: bitty-1.0.0-rc3.full.js
Overview

bitty-js is a web component. It uses signals to make pages reactive. For example:

Waiting for click

The bitty-js API is made from data-* attributes. For example, here's the HTML that makes up the example:

<bitty-js data-connect="/modules/timestamp-demo.js">
  <button data-send="updateTimestamp">Show Timestamp</button>
  <div data-receive="updateTimestamp">Waiting for click</div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>

Functionality

The update values from the data-send and data-receive attributes map to a function in the module with the same name.

export default class {
  updateTimestamp(event, element) {
    element.innerHTML = Date().toString();
  }
}

The arguments passed to the function are:

  1. The event that triggered the update (e.g. click events on the button in the example)

  2. The element to update (e.g. the div element in the example)

The event and element are the same ones you'd get from document.addEventListener() and document.querySelector() functions.

When the example button is clicked the div element is passed through the function where it has its innerHTML updated. (The event isn't used in this example.)

Basic Examples

This is a collection of examples showing some of the ways bitty-js can be used. The examples all work. The source code for each is available in the Show Example Code sections.

Send/Receive

Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/send-and-receive.js">
  <div data-receive="updateSendAndReceive">Waiting for click</div>
  <button data-send="updateSendAndReceive">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/send-and-receive.js
export default class {
  updateSendAndReceive(_event, element) {
    element.innerHTML = Date.now();
  }
}
ID: 0010-send-and-receive

Sending Multiple Signals

Waiting for Alfa
Waiting for Bravo
Waiting for Charlie
Show Example Code
example.html
<bitty-js data-connect="/modules/send-multiple-signals.js">
  <div>
    <div data-receive="multiAlfa">Waiting for Alfa</div>
    <div data-receive="multiBravo">Waiting for Bravo</div>
    <div data-receive="multiCharlie">Waiting for Charlie</div>
  </div>
  <button data-send="multiAlfa|multiBravo|multiCharlie">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/send-multiple-signals.js
export default class {
  multiAlfa(_event, element) {
    element.innerHTML = Date.now();
  }

  multiBravo(_event, element) {
    element.innerHTML = Date.now();
  }

  multiCharlie(_event, element) {
    element.innerHTML = Date.now();
  }
}
ID: 0015-send-multiple-signals

Receiving Multiple Signals

Waiting for clicks
Show Example Code
example.html
<bitty-js data-connect="/modules/receive-multiple-signals.js">
  <div data-receive="multiReceiveAlfa|multiReceiveBravo|multiReceiveCharlie">Waiting for clicks</div>
  <div>
    <button data-send="multiReceiveAlfa">Send Alfa</button>
    <button data-send="multiReceiveBravo">Send Bravo</button>
    <button data-send="multiReceiveCharlie">Send Charlie</button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/receive-multiple-signals.js
export default class {
  multiReceiveAlfa(_event, element) {
    element.innerHTML = `Alfa`;
  }

  multiReceiveBravo(_event, element) {
    element.innerHTML = `Bravo`;
  }

  multiReceiveCharlie(_event, element) {
    element.innerHTML = `Charlie`;
  }
}
ID: 0017-recieve-multiple-signals

Using Additional Data Attributes

Waiting for clicks
Show Example Code
example.html
<bitty-js data-connect="/modules/additional-attributes.js">
  <div data-receive="updateFromData">Waiting for clicks</div>
  <div>
    <button data-color="red" data-send="updateFromData">red</button>
    <button data-color="green" data-send="updateFromData">green</button>
    <button data-color="blue" data-send="updateFromData">blue</button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/additional-attributes.js
export default class {
  updateFromData(event, element) {
    const selectedColor = event.target.dataset.color;
    ["red", "green", "blue"].forEach((color) => {
      if (color === selectedColor) {
        element.classList.add(color);
      } else {
        element.classList.remove(color);
      }
    });
    element.innerHTML = `Selected: ${selectedColor}`;
  }
}
ID: 0018-additional-attributes

Multiple Senders and Multiple Receivers

Waiting for click
Waiting for click
Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/multiple-senders-and-multiple-receivers.js">
  <div>
    <div data-receive="bookTitle">Waiting for click</div>
    <div data-receive="bookAuthor">Waiting for click</div>
    <div data-receive="bookPages">Waiting for click</div>
  </div>
  <div>
    <button data-type="fiction" data-send="setBookType|bookTitle|bookAuthor|bookPages">Fiction</button>
    <button data-type="nonFiction" data-send="setBookType|bookTitle|bookAuthor|bookPages">Non-Fiction</button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/multiple-senders-and-multiple-receivers.js
export default class {
  #books = {
    fiction: {
      title: "Neuromancer",
      author: "William Gibson",
      pages: 271,
    },
    nonFiction: {
      title: "On Writing",
      author: "Stephen King",
      pages: 288,
    },
  };
  #bookType = null;

  bookAuthor(_event, element) {
    element.innerHTML = `Author: ${this.#books[this.#bookType].author}`;
  }

  bookPages(_event, element) {
    element.innerHTML = `Pages: ${this.#books[this.#bookType].pages}`;
  }

  setBookType(event, _element) {
    this.#bookType = event.target.dataset.type;
  }

  bookTitle(_event, element) {
    element.innerHTML = `Title: ${this.#books[this.#bookType].title}`;
  }
}
ID: 0019-multiple-senders-and-multiple-receivers

Forward an Event Inside a Module

Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/forward-inside-a-module.js">
  <div data-receive="forwardedSignal">Waiting for click</div>
  <button data-send="forwardUpdate">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/forward-inside-a-module.js
export default class {
  forwardedSignal(_event, element) {
    element.innerHTML = Date.now();
  }

  forwardUpdate(event, _element) {
    this.api.forward(event, "forwardedSignal");
  }
}
ID: 0020-forward-inside-a-module

Sending Directly from the bitty-js Element

Show Example Code
example.html
<bitty-js data-connect="/modules/send-from-bitty.js" data-send="updateFromBitty">
  <div data-receive="updateFromBitty"></div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/send-from-bitty.js
export default class {
  updateFromBitty(_event, element) {
    element.innerHTML = `Initialized at ${Date().toString()}`;
  }
}
ID: 0023-sending-from-bitty

Storing State

Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/storing-state.js">
  <div data-receive="updateStoredState">Waiting for click</div>
  <button data-send="updateStoredState">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/storing-state.js
export default class {
  #currentState = "Off";

  updateStoredState(_event, element) {
    if (this.#currentState === "Off") {
      this.#currentState = "On";
    } else {
      this.#currentState = "Off";
    }
    element.innerHTML = this.#currentState;
  }
}
ID: 0025-storing-state

Calling Functions Without Receivers

Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/calling-functions.js">
  <div>Waiting for click</div>
  <button data-send="updateWithoutReceiver">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/calling-functions.js
export default class {
  updateWithoutReceiver(_event, _element) {
    this.api.querySelector("div").innerHTML = Date.now();
  }
}
ID: 0030-calling-functions

Updating An Element Directly

Show Example Code
example.html
<bitty-js data-connect="/modules/updating-self.js">
  <button data-send="updateSelf" data-receive="updateSelf">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/updating-self.js
export default class {
  updateSelf(_event, element) {
    element.innerHTML = Date.now();
  }
}
ID: 0040-updating-self

Using Event Data

Waiting on click
Show Example Code
example.html
<bitty-js data-connect="/modules/using-event-data.js">
  <div data-receive="updateWithEventData">Waiting on click</div>
  <button data-send="updateWithEventData">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/using-event-data.js
export default class {
  updateWithEventData(event, element) {
    element.innerHTML = `${event.type} ${Date.now()}`;
  }
}
ID: 0043-using-event-data

Using Radio Buttons

Waiting
Show Example Code
example.html
<bitty-js data-connect="/modules/using-radio-buttons.js">
  <div data-receive="updateFromRadioButtons">Waiting</div>
  <label>
    Alfa
    <input
      data-send="updateFromRadioButtons"
      value="Alfa"
      type="radio"
      name="radio-example"
    />
  </label>
  <label>
    Bravo
    <input
      data-send="updateFromRadioButtons"
      value="Bravo"
      type="radio"
      name="radio-example"
    />
  </label>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/using-radio-buttons.js
export default class {
    updateFromRadioButtons(event, element) {
        element.innerHTML = `Selected: ${event.target.value}`;
    }
}
ID: 0044-using-radio-buttons

Using Input Text Elements

Waiting
Show Example Code
example.html
<bitty-js data-connect="/modules/using-input-text-elements.js">
  <div data-receive="updateFromTextInput">Waiting</div>
  <input type="text" data-send="updateFromTextInput" />
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/using-input-text-elements.js
export default class {
  updateFromTextInput(event, element) {
    if (event.target.value !== "") {
      element.innerHTML = event.target.value;
    }
  }
}
ID: 0045-using-input-text-elements

Range Sliders

Waiting
Show Example Code
example.html
<bitty-js data-connect="/modules/range-sliders.js">
  <div data-receive="updateFromRangeSlider">Waiting</div>
  <label>
    Send
    <input data-send="updateFromRangeSlider" type="range" />
  </label>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/updating-range-sliders.js

            
ID: 0050-range-sliders

Automatically Added UUIDs

Waiting for click
---
Show Example Code
example.html
<bitty-js data-connect="/modules/element-uuids.js">
  <div data-receive="uuidDemo">Waiting for click <br />---</div>
  <div>
    <button data-send="uuidDemo">Alfa</button>
    <button data-send="uuidDemo">Bravo</button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/element-uuids.js
export default class {
  uuidDemo(event, element) {
    element.innerHTML = `<div>From: ${event.target.dataset.uuid}</div>
        <div>To: ${element.dataset.uuid}</div>`;
  }
}
ID: 0060-element-uuids

Avoiding Text Input Feedback


Show Example Code
example.html
<bitty-js data-connect="/modules/avoiding-text-input-feedback.js">
  <label>
    Alfa
    <input type="text" data-send="avoidInputFeedback" data-receive="avoidInputFeedback" />
  </label>
  <br>
  <label>
    Bravo
    <input type="text" data-send="avoidInputFeedback" data-receive="avoidInputFeedback" />
  </label>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/avoiding-text-input-feedback.js
export default class {
  avoidInputFeedback(event, element) {
    if (element.dataset.uuid !== event.target.dataset.uuid) {
      element.value = event.target.value;
    }
  }
}
ID: 0070-avoiding-text-input-feedback.txt

Avoiding Range Slider Feedback


Show Example Code
example.html
<bitty-js data-connect="/modules/avoiding-range-slider-feedback.js">
  <label>
    Alfa
    <input data-send="updateSliderFeedback" data-receive="updateSliderFeedback" type="range" />
  </label>
  <br />
  <label>
    Bravo
    <input data-send="updateSliderFeedback" data-receive="updateSliderFeedback" type="range" />
  </label>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/avoiding-range-slider-feedback.js
export default class {
  updateSliderFeedback(event, element) {
    if (element.dataset.uuid !== event.target.dataset.uuid) {
      element.value = event.target.value;
    }
  }
}
ID: 0075-avoiding-range-slider-feedback

Initializing Values

Show Example Code
example.html
<bitty-js data-connect="/modules/initialize-values.js">
  <label>
    Initialized to 90
    <input type="range" />
  </label>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/initialize-values.js
export default class {
  bittyInit() {
    [...this.api.querySelectorAll("[type=range]")]
      .forEach((element) => {
        element.value = 90;
      });
  }
}
ID: 0100-initialize-values

Using Templates

Show Example Code
example.html
<bitty-js data-connect="/modules/using-templates.js"></bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/using-templates.js
const template = document.createElement("template");
template.innerHTML =
  `<button data-send="updateFromTemplate" data-receive="updateFromTemplate">Click Me</button>`;

export default class {
  bittyInit() {
    this.api.replaceChildren(template.content.cloneNode(true));
  }

  updateFromTemplate(_event, element) {
    element.innerHTML = Date.now();
  }
}
ID: 0115-using-templates

Create Elements

Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/create-elements.js">
  <div data-receive="createElements">
    Waiting for click
  </div>
  <div data-receive="elementsAdded">
    <button data-send="createElements|elementsAdded">
        Create Elements
    </button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/create-elements.js
export default class {
  createElements(_event, element) {
    const display = document.createElement("div");
    display.innerHTML = "Waiting for slider";
    display.dataset.receive = "update";
    element.replaceChildren(display);
    const slider = document.createElement("input");
    slider.type = "range";
    slider.value = "0";
    slider.dataset.send = "update";
    element.appendChild(slider);
  }

  elementsAdded(_event, element) {
    element.innerHTML = "Elements added";
  }

  update(event, element) {
    element.innerHTML = event.target.value;
  }
}
ID: 0120-create-elements

Copy Button

The quick brown fox
jumps over
the lazy dog
Show Example Code
example.html
<bitty-js data-connect="/modules/copy-button.js">
  <pre id="text-to-copy">
The quick brown fox
jumps over
the lazy dog</pre
  >
  <div>
    <button data-target="#text-to-copy" data-send="copyText">Copy Text</button>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/copy-button.js
export default class {
  #theTimeout = null;
  #originalText = "Copy";

  async copyText(event, _element) {
    const elToCopy = document.querySelector(event.target.dataset.target);
    if (event.target.innerHTML !== "Copied") {
      this.#originalText = event.target.innerHTML;
    }
    try {
      let content;
      if (elToCopy.value) {
        content = elToCopy.value;
      } else {
        content = elToCopy.innerText;
      }
      await navigator.clipboard.writeText(content);
      event.target.innerHTML = "Copied";
    } catch (err) {
      event.target.innerHTML = "Error copying";
    }
    if (this.#theTimeout !== null) {
      clearTimeout(this.#theTimeout);
    }
    this.#theTimeout = setTimeout(
      (theButton) => {
        event.target.innerHTML = this.#originalText;
      },
      2000,
      event.target,
    );
  }
}
ID: 0130-copy-button

Customize Event Listeners

Waiting for mouse
Show Example Code
example.html
<bitty-js data-connect="/modules/alternate-event-listeners.js"
  data-listeners="mouseover|mouseout">
  <button data-send="sawMouse">
    Mouseover
  </button>
  <div data-receive="sawMouse">
    Waiting for mouse
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/alternate-event-listeners.js
export default class {
  sawMouse(event, element) {
    element.innerHTML = `${event.type} at ${Date.now()}`;
  }
}
ID: 0190-alternate-event-listeners

Multiple Class Exports

Waiting for click
Waiting for click
Show Example Code
example.html
<bitty-js data-connect="/modules/multiple-class-exports.js">
  <button data-send="updateDefaultClass">Default Class</button>
  <div data-receive="updateDefaultClass">Waiting for click</div>
</bitty-js>

<bitty-js data-connect="/modules/multiple-class-exports.js|AlternativeClass">
  <button data-send="updateAltClass">Alternative Class</button>
  <div data-receive="updateAltClass">Waiting for click</div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
modules/multiple-class-exports.js
export default class {
  updateDefaultClass(_event, element) {
    element.innerHTML = Date.now();
  }
}

export class AlternativeClass {
  updateAltClass(_event, element) {
    element.innerHTML = Date.now();
  }
}
ID: 0200-multiple-class-exports
Multiple Instances

Instance Isolation

Waiting
Waiting
Code
<bitty-js data-connect="/modules/instance-isolation.js">
  <button data-send="updateIsolatedInstance">Click Me</button>
  <div data-receive="updateIsolatedInstance">Waiting</div>
</bitty-js>

<bitty-js data-connect="/modules/instance-isolation.js">
  <button data-send="updateIsolatedInstance">Click Me</button>
  <div data-receive="updateIsolatedInstance">Waiting</div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
export default class {
  #value = 0;

  updateIsolatedInstance(event, element) {
    // The same signal name is used in both
    // elements in this example. Separating
    // the data can be done by comparing
    // the bitty-js component they are
    // connect to with the data from
    // the triggering event.

    // get the uuid of the parent bitty-js
    // component
    const bittyUUID = this.api.dataset.uuid;

    // get the uuid of the parent element from
    // the event target
    const checkUUID = event.target.parentNode.dataset.uuid;

    if (bittyUUID === checkUUID) {
      this.#value += 1;
      element.innerHTML = this.#value;
    }
  }
}

Sending Signals From Parents To Children

Waiting
Code
<bitty-js data-connect="/modules/sending-signals-from-parents-to-children.js">  
  <div>
    <button data-send="demoParentToChildSignal">
      Click Me
    </button>
  </div>

  <div>
    <bitty-js 
      data-connect="/modules/sending-signals-from-parents-to-children.js">
      <div  data-receive="demoParentToChildSignal">
        Waiting
      </div>
    </bitty-js>
  </div>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
export default class {
  demoParentToChildSignal(event, element) {
    element.innerHTML = Date.now();
  }
}

Sending Signals from Children to Parents

Waiting
Code
<bitty-js data-connect="/modules/sending-signals-up.js|Parent">
    <div data-receive="demoSendingFromChildToParent">Waiting</div>
    <bitty-js data-connect="/modules/sending-signals-up.js|Child">  
      <button data-send="demoSendingFromChildToParent">Click Me</button>
    </bitty-js>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
export class Parent {
  demoSendingFromChildToParent(_event, element) {
    element.innerHTML = Date.now();
  }
}

export class Child {
  // must exist, but no methods required
}

Sending Signals Across The DOM

Code
<bitty-js data-connect="/modules/sending-across-the-dom.js">
  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <ul>
    <li>
      <bitty-js data-connect="/modules/sending-across-the-dom.js">
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
      </bitty-js>
      <ul>
        <li>
        <bitty-js data-connect="/modules/sending-across-the-dom.js">
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        </bitty-js>
          <ul>
            <li>
            <bitty-js data-connect="/modules/sending-across-the-dom.js">
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
            </bitty-js>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>

  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
  <ul>
    <li>
      <bitty-js data-connect="/modules/sending-across-the-dom.js">
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
      </bitty-js>
      <ul>
        <li>
        <bitty-js data-connect="/modules/sending-across-the-dom.js">
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
          <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
        </bitty-js>
          <ul>
            <li>
            <bitty-js data-connect="/modules/sending-across-the-dom.js">
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
              <button data-send="coverageDemo" data-receive="coverageDemo">Waiting</button>
            </bitty-js>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>

Storing Data In Parent

-
Code
<bitty-js data-connect="/modules/storing-data-in-parent.js|Parent">

  <div data-receive="incrementDemo|decrementDemo">-</div>

  <bitty-js data-connect="/modules/storing-data-in-parent.js|Child">
    <button data-send="incrementDemo">Increment</button>
  </bitty-js>

  <bitty-js data-connect="/modules/storing-data-in-parent.js|Child">
    <button data-send="decrementDemo">Decrement</button>
  </bitty-js>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
export class Parent {
  #value = 0;
  decrementDemo(_event, element) {
    this.#value -= 1;
    element.innerHTML = this.#value;
  }
  incrementDemo(_event, element) {
    this.#value += 1;
    element.innerHTML = this.#value;
  }
}

export class Child {
  // no methods required
}

Share Global Data Between Siblings

Code
<bitty-js data-connect="/modules/sharing-global-data.js">
  <button data-send="globalDataExample" data-receive="globalDataExample">Click Me</button>
</bitty-js>

<bitty-js data-connect="/modules/sharing-global-data.js">
  <button data-send="globalDataExample" data-receive="globalDataExample">Click Me</button>
</bitty-js>
<script type="module" src="bitty-1.0.0-rc3.min.js"></script>
Loading Functionality from the Page

bitty-js can use <script> tags on the page for functinoality. For example, this <script> is in the <head> element of this page:

<script>
window.bittyClasses = {};
bittyClasses.InlineExample = class { 
  updateFromPage(event, element) {
    element.innerHTML = Date.now();
  }
};
</script>

Using it is done by setting the data-connect attribute of a bitty-js to the name of the class in the bittyClasses object:

<bitty-js data-connect="InlineExample">
  <div data-receive="updateFromPage">Waiting for click</div>
  <button data-send="updateFromPage">Click Me</button>
</bitty-js>

Here's what it looks like:

Waiting for click
Documentation In Progress

Docs are in the process of being overhauled. Check out the code from the examples and test cases for the time being.

Release Notes

Version: 1.0.0-rc3

Sept. 23, 2025

Version: 1.0.0-rc2

Sept. 19, 2025

Version: 1.0.0-rc1

Sept. 18, 2025

Initial release candidate for version 1.0.0.

No changes since v0.4.0.

Version: 0.4.0

Sept. 17, 2025

Version: 0.3.0

Sept. 17, 2025

Big refactor based on experience using 0.2.0.

Version: 0.2.3

June 5, 2025

Lots of error handling work in this one.

Version: 0.2.2

June 4, 2025

Added UUIDs for pending error handling. Shuffeld a bunch of stuff around to make the examples look nicer.

Version: 0.2.1

June 3, 2025

Some more wiring and some support tools for making the demo/docs site.

Version: 0.2.0

June 3, 2025

Setting up a bunch of the basic wiring.

Version: 0.1.0

June 2, 2025

Getting the project started.

Test Suite

Test Results

bittyInit is called automatically
FAILED
Test Code
ID: 0005-bitty-init-is-called
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <div class="test">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("div").innerHTML = "PASSED";
  }
}
Basic Send and Receive
FAILED
Test Code
ID: 0010-basic-send-receive
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0010">Test Trigger</button>
  <div class="test" data-receive="runTest0010">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0010(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Nest Element Sending and Receiveing
---
  • FAILED
Test Code
ID: 0015-nestined-send-and-receive
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <ul>
    <li><button data-send="runTest0015">Test Trigger</button></li>
  </ul>
  <div>---</div>
  <ul>
    <li><div class="test" data-receive="runTest0015">FAILED</div></li>
  </ul>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0015(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Send from the bitty-js element
FAILED
Test Code
ID: 0020-send-from-bitty-js
<bitty-js data-connect="/tests/[@ folder.name @]/test.js" data-send="runTest0020">
  <div class="test" data-receive="runTest0020">FAILED</div>
</bitty-js>
export default class {
  runTest0020(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Multiple sends from a single element
FAILED
FAILED
FAILED
Test Code
ID: 0030-send-multiple-signals
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTestAlfa0030|runTestBravo0030|runTestCharlie0030">Test Trigger</button>
  <div class="test" data-receive="runTestAlfa0030">FAILED</div>
  <div class="test" data-receive="runTestBravo0030">FAILED</div>
  <div class="test" data-receive="runTestCharlie0030">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTestAlfa0030(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestBravo0030(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestCharlie0030(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Multiple Receivers for a Single Sender
FAILED
FAILED
FAILED
Test Code
ID: 0040-multiple-receivers
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0040">Test Trigger</button>
  <div class="test" data-receive="runTest0040">FAILED</div>
  <div class="test" data-receive="runTest0040">FAILED</div>
  <div class="test" data-receive="runTest0040">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTest0040(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Access data-* Attributes from Sending Elements
FAILED
Test Code
ID: 0050-accessing-data-from-sending-element
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-status="PASSED" data-send="runTest50">Test Trigger</button>
  <div class="test" data-receive="runTest50">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest50(event, element) {
    element.innerHTML = event.target.dataset.status;
  }
}
Access data-* Attributes from Receiving Elements
FAILED
Test Code
ID: 0060-access-data-from-receiving-element
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0060">Test Trigger</button>
  <div class="test" data-status="PASSED" data-receive="runTest0060">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTest0060(_event, el) {
    el.innerHTML = el.dataset.status;
  }
}
Sending Many Signals to Many Receivers
FAILED
FAILED
FAILED
Test Code
ID: 0070-many-sends-to-many-receivers
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTestAlfa0070|runTestBravo0070|runTestCharlie0070">Test Trigger</button>
  <div class="test" data-receive="runTestAlfa0070">FAILED</div>
  <div class="test" data-receive="runTestBravo0070">FAILED</div>
  <div class="test" data-receive="runTestCharlie0070">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTestAlfa0070(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestBravo0070(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestCharlie0070(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Forward an Event Inside a Module
FAILED
Test Code
ID: 0075-forward-inside-module
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0075">Test Trigger</button>
  <div class="test" data-receive="forwardEvent0075">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0075(event, _el) {
    this.api.forward(event, "forwardEvent0075");
  }

  forwardEvent0075(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Storing State
FAILED
Test Code
ID: 0080-storing-state
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0080">Test Trigger</button>
  <div class="test" data-receive="runTest0080">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  #count = 0;
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
    this.api.querySelector("button").click();
    this.api.querySelector("button").click();
  }

  runTest0080(_event, el) {
    this.#count += 1;
    if (this.#count === 3) {
      el.innerHTML = "PASSED";
    }
  }
}
Sending and Receiving from the Same Element
Test Code
ID: 0090-updating-self-loopback-signal
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button class="test" data-send="runTest0090" data-receive="runTest0090">Test Trigger</button>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    const button = this.api.querySelector("button");
    button.click();
  }
  runTest0090(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Get Event Type
FAILED
Test Code
ID: 0100-get-event-type
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0100">Test Trigger</button>
  <div class="test" data-receive="runTest0100">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTest0100(event, el) {
    if (event.type === "click") {
      el.innerHTML = "PASSED";
    }
  }
}
Verify UUIDs are Created on Sending Elements
FAILED
Test Code
ID: 0110-verify-uuids-exist-on-senders
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0110">Test Trigger</button>
  <div class="test" data-receive="runTest0110">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0110(event, el) {
    if (event.target.dataset.uuid !== undefined) {
      el.innerHTML = "PASSED";
    }
  }
}
Verify UUIDs are Created on Receiving Elements
FAILED
Test Code
ID: 0120-verify-uuids-exist-on-receivers
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0120">Test Trigger</button>
  <div class="test" data-receive="runTest0120">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0120(_event, element) {
    if (element.dataset.uuid !== undefined) {
      element.innerHTML = "PASSED";
    }
  }
}
Verify UUIDs Exist On All Elements
FAILED
Test Code
ID: 0125-uuids-exist-on-all-elements
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <div class="test">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    const el = this.api.querySelector("div");
    if (el.dataset.uuid) {
      el.innerHTML = "PASSED";
    }
  }
}
Verify UUIDs Are Added To Events
FAILED
Test Code
ID: 0127-uuids-exist-on-events
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0127">Test Trigger</button>
  <div class="test" data-receive="runTest0127">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0127(event, element) {
    if (event.uuid !== undefined) {
      element.innerHTML = "PASSED";
    }
  }
}
Existing data-uuid Attributes Aren't Overwritten
FAILED
FAILED
Test Code
ID: 0128-uuids-are-not-overwritten
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <div class="test" data-uuid="original-uuid">FAILED</div>
  <div class="test" data-receive="testStub" data-uuid="original-uuid">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    const els = this.api.querySelectorAll("[data-uuid]");
    [...els].forEach((el) => {
      if (el.dataset.uuid === "original-uuid") {
        el.innerHTML = "PASSED";
      }
    });
  }
}
Ensure New Elements Get a data-uuid if They Have data-send/receive
FAILED
Test Code
ID: 0130-create-an-element
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0130">Test Trigger</button>
  <div class="test-element-wrapper" data-receive="runTest0130">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  async runTest0130(_event, element) {
    const newButton = document.createElement("button");
    newButton.dataset.receive = "placeholderToGenerateUUID";
    newButton.innerHTML = "FAILED";
    element.replaceChildren(newButton);
    // sleep for test to wait for observer
    // to update the UUID.
    await sleep(200);
    if (newButton.dataset.uuid !== undefined) {
      newButton.innerHTML = "PASSED";
      newButton.classList.add("test");
    }
  }
}
Ensure Elements Made from Templates Get UUIDs
FAILED
Test Code
ID: 0140-ensure-template-elements-get-uuids
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0140">Test Trigger</button>
  <div class="test-element-wrapper" data-receive="runTest0140">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const template = document.createElement("template");
template.innerHTML = `<button class="test" data-send="testStub">FAILED</button>`;

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  async runTest0140(_event, element) {
    let newButton = template.content.cloneNode(true);
    element.replaceChildren(newButton);
    // sleep for test to wait for observer
    // to update the UUID.
    await sleep(100);
    if (element.childNodes[0].dataset.uuid !== undefined) {
      element.childNodes[0].innerHTML = "PASSED";
      element.childNodes[0].classList.add("test");
    }
  }
}
Use Named (Non-Default) Exported Classes
FAILED
Test Code
ID: 0150-use-non-default-classes
<bitty-js data-connect="/tests/[@ folder.name @]/test.js|AltClass">
  <button data-send="runTest0150">Test Trigger</button>
  <div class="test" data-receive="runTest0150">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export class AltClass {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0150(_event, el) {
    el.innerHTML = "PASSED";
  }
}
Verify the bittyInit() Function Explicitly
FAILED
Test Code
ID: 0160-init-function-works
<bitty-js data-connect="/tests/[@ folder.name @]/test.js" class="test">FAILED</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.innerHTML = "PASSED";
  }
}
Use data-send To Call a Function When There's No Element With a Corresponding Receiver
FAILED
Test Code
ID: 0170-use-data-send-without-a-receiver
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
  <button data-send="runTest0170">Test Trigger</button>
  <div class="test">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0170(event, _el) {
    this.api.querySelector("div").innerHTML = "PASSED";
  }
}
Updating Children Instances from Parent Instance
FAILED
Test Code
ID: 0180-updating-children-from-parent
<bitty-js data-connect="/tests/[@ folder.name @]/test.js|Parent">
  <button data-send="runTest0180">Test Trigger</button>
  <bitty-js data-connect="/tests/[@ folder.name @]/test.js|Child">
    <div class="test" data-receive="runTest0180">FAILED</div>
  </bitty-js>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export class Parent {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }
  runTest0180(_event, element) {
    element.innerHTML = `PASSED`;
  }
}

export class Child {
  // must exist, but no methods required
}
Parents Receive Child Component Signals
FAILED
Test Code
ID: 0200-parent-receive-child-component-signlas
<bitty-js data-connect="/tests/[@ folder.name @]/test.js|Parent">
  <div class="test" data-receive="runTest0200">FAILED</div>
  <bitty-js data-connect="/tests/[@ folder.name @]/test.js|Child">
    <button data-send="runTest0200">Test Trigger</button>
  </bitty-js>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export class Parent {
  async bittyInit() {
    await sleep(100) // time pad for test
    this.api.querySelector("button").click();
  }

  runTest0200(event, element) {
    element.innerHTML = "PASSED";
  }
}

export class Child {
  // must exist, but no methods required
}
Siblings Can Receive Signals From Each Other
FAILED
Test Code
ID: 0210-sibling-watch-signals
<bitty-js data-connect="/tests/[@ folder.name @]/test.js|Alfa">
  <div class="test" data-receive="runTest0210">FAILED</div>
</bitty-js>

<bitty-js data-connect="/tests/[@ folder.name @]/test.js|Bravo">
  <button data-send="runTest0210">Test Trigger</button>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export class Alfa {
  runTest0210(event, element) {
    element.innerHTML = "PASSED";
  }
}

export class Bravo {
  async bittyInit() {
    await sleep(100) // time pad for test
    const btn = this.api.querySelector("button");
    btn.click();
  }
}
Send Signals Up and Down with data-watch
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
FAILED
Test Code
ID: 0215-send-signals-up-and-down-with-watch
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">

  <div class="test" data-receive="runTest0215Echo">FAILED</div>
  <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildAlfa">
    <div class="test" data-receive="runTest0215Alfa">FAILED</div>
    <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildBravo">
      <div class="test" data-receive="runTest0215Bravo">FAILED</div>

      <button data-send="runTest0215Alfa|runTest0215Bravo|runTest0215Charlie|runTest0215Delta|runTest0215Echo">Test Trigger</button>

      <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildCharlie">
        <div class="test" data-receive="runTest0215Charlie">FAILED</div>
        <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildDelta">
          <div class="test" data-receive="runTest0215Delta">FAILED</div>
        </bitty-js>
      </bitty-js>
    </bitty-js>
  </bitty-js>

  <div class="test" data-receive="runTest0215Echo">FAILED</div>
  <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildAlfa">
    <div class="test" data-receive="runTest0215Alfa">FAILED</div>
    <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildBravo">
      <div class="test" data-receive="runTest0215Bravo">FAILED</div>
      <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildCharlie">
        <div class="test" data-receive="runTest0215Charlie">FAILED</div>
        <bitty-js data-connect="/tests/[@ folder.name @]/test.js|ChildDelta">
          <div class="test" data-receive="runTest0215Delta">FAILED</div>
        </bitty-js>
      </bitty-js>
    </bitty-js>
  </bitty-js>

</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    const btn = this.api.querySelector("button");
    btn.click();
  }

  runTest0215Echo(_event, element) {
    element.innerHTML = "PASSED";
  }
}

export class ChildAlfa {
  runTest0215Alfa(_event, element) {
    element.innerHTML = "PASSED";
  }
}

export class ChildBravo {
  runTest0215Bravo(_event, element) {
    element.innerHTML = "PASSED";
  }
}

export class ChildCharlie {
  runTest0215Charlie(_event, element) {
    element.innerHTML = "PASSED";
  }
}

export class ChildDelta {
  runTest0215Delta(_event, element) {
    element.innerHTML = "PASSED";
  }
}
Chain this.api.forward() calls
FAILED
FAILED
FAILED
FAILED
Test Code
ID: 0220-chain-forword-api-calls
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
    <button data-send="runTest0220">Test Trigger</button>
    <div class="test" data-receive="runTest0220">FAILED</div>
    <div class="test" data-receive="secondSignal0220">FAILED</div>
    <div class="test" data-receive="thirdSignal0220">FAILED</div>
    <div class="test" data-receive="fourthSignal0220">FAILED</div>
</bitty-js>
// Ensure the `data-send` value of the
// event generating element doesn't
// get changed
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}


export default class {
  #counterAlfa = 0;
  #counterBravo = 0;
  #counterCharlie = 0;
  #counterDelta = 0;

  async bittyInit() {
    await sleep(100) // time pad for test
    const button = this.api.querySelector("button");
    button.click();
    button.click();
    button.click();
    button.click();
  }

  runTest0220(event, element) {
    this.#counterAlfa += 1;
    event.target.dataset.counter = `${this.#counterAlfa}`;
    if (event.target.dataset.counter == "4") {
      element.innerHTML = "PASSED";
    }
    this.api.forward(event, "secondSignal0220");
  }

  secondSignal0220(event, element) {
    this.#counterBravo += 1;
    event.target.dataset.counter = `${this.#counterBravo}`;
    if (event.target.dataset.counter == "4") {
      element.innerHTML = "PASSED";
    }
    this.api.forward(event, "thirdSignal0220");
  }

  thirdSignal0220(event, element) {
    this.#counterCharlie += 1;
    event.target.dataset.counter = `${this.#counterCharlie}`;
    if (event.target.dataset.counter == "4") {
      element.innerHTML = "PASSED";
    }
    this.api.forward(event, "fourthSignal0220");
  }

  fourthSignal0220(event, element) {
    this.#counterDelta += 1;
    event.target.dataset.counter = `${this.#counterDelta}`;
    if (event.target.dataset.counter == "4") {
      element.innerHTML = "PASSED";
    }
  }

  //
}
Forward Multiple Signals
FAILED
FAILED
FAILED
FAILED
Test Code
ID: 0230-forward-multiple-signals
<bitty-js data-connect="/tests/[@ folder.name @]/test.js">
    <button data-send="runTest0230">Test Trigger</button>
    <div class="test" data-receive="runTest0230">FAILED</div>
    <div class="test" data-receive="secondSignal0230">FAILED</div>
    <div class="test" data-receive="thirdSignal0230">FAILED</div>
    <div class="test" data-receive="fourthSignal0230">FAILED</div>
</bitty-js>
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100) // time pad for test
    const button = this.api.querySelector("button");
    button.click();
  }

  runTest0230(event, element) {
    element.innerHTML = "PASSED";
    this.api.forward(event, "secondSignal0230|thirdSignal0230|fourthSignal0230");
  }

  secondSignal0230(_event, element) {
    element.innerHTML = "PASSED";
  }

  thirdSignal0230(_event, element) {
    element.innerHTML = "PASSED";
  }

  fourthSignal0230(_event, element) {
    element.innerHTML = "PASSED";
  }
}
Change Event Listeners
Manual Test By Mousing Over
Test Code
ID: 0240-change-event-listeners
<bitty-js 
  data-connect="/tests/[@ folder.name @]/test.js"
  data-listeners="mouseover|mouseout"
>
  <button data-send="runTest0240">Test Target For Mouseover</button>
  <div data-receive="runTest0240">Manual Test By Mousing Over</div>
</bitty-js>
export default class {
  runTest0240(_event, element) {
    element.innerHTML = "PASSED";
    element.classList.add("test-passed");
  }
}
Test TODO List
- Test receivers are properly removed
  when their elements are removed from the tree 
Test Code
ID: 9000-todo
<pre>
- Test receivers are properly removed
  when their elements are removed from the tree 
</pre>
// no code for test todo stub
License

MIT License

Copyright (c) 2025 Alan Smith - https://bitty-js.alanwsmith.com/

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice, this permission notice, and the ID "2y1pBoEREr3eWA1ubCCOXdmRCdn" shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Feedback Requested

bitty-js is currently bitty-1.0.0-rc3.min.js. The API is stable. Aside from bug fixes, it will become version 1.0. Play around with it. Let me know if you find bugs or show-stoppers.

Happy Coding

I put a lot of thought and effort into bitty-js. All of it aimed at making things simpler. I'm really proud of where it ended up. I hope it helps you enjoy making things for the web.

- alan