bitty

a bit of interactivity
Introduction

bitty is a web component. Wrapping it around other elements makes them interactive. For example, click/tap this button a few times in differnet places to get the coordinates of where the interaction occurred:

Example
waiting

Here's the code behind the functionality:

/v5.0.0/modules/get-coords.js
export default class {
  coords(event, el) {
    el.innerHTML = `x: ${event.offsetX} - y: ${event.offsetY}`;
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-coords.js">
  <button data-send="coords">Get Click Coordinates</button>
  <div data-receive="coords">waiting</div>
</bitty-5-0>

<script src="/release/bitty-5.0.0.min.js" type="module"></script>

How It Works

bitty uses signals to associate events with elements. They're connected through methods you write to provide functionality. For example:

  • <button data-send="coords"> sends a signal named coords when it's clicked
  • <div data-receive="coords"> receives coords signals
  • coords(event, el) {} sets the .innerHTML value of the <div> to the .offsetX and .offsetY values from the click event of the coords signal.

    (Signal names map to method names which is why this one is named coords.)

That's pretty much it. bitty has some helper functions. They do things like load external HTML, JSON, and SVG files, etc... but that's all secondary. At its core, bitty sets you up to create your functionality and then gets out of the way.

Installation / Getting Started

TL;DR

This section walks through making these two files. If you just want to see them work you can copy the contents, adjust the paths as necessary, and drop them on your site.

page.html
<bitty-5-0 data-connect="/v5.0.0/modules/installation-example-1.js">
  <button data-send="randomNumTake1" data-receive="randomNumTake1">
    Get Random Number
  </button>
</bitty-5-0>
<script src="/releases/bitty-5.0.0.min.js" type="module"></script>  
/v5.0.0/modules/installation-example-1.js
export default class {
  randomNumTake1(_event, el) {
    el.innerHTML = Math.random();
  }
}

The result is this button that updates with a random number:

The Walk-Through

The <script> Tag

bitty is a web component. Including it on your page is done with a script tag:

  • download the bitty-5.0.0.min.js file
  • put the file somewhere in your site's directory tree
  • load bitty on your pages with a script tag like:

    <script src="/releases/bitty-5.0.0.min.js" type="module"></script>

Making a Functionality Class

bitty works by connection to a class that provides methods for functionality. For example, here's a module that exports a default class with a randomNum() method in it.

/v5.0.0/modules/installation-example-2.js
export default class {
  randomNumTake2(_event, el) {
    el.innerHTML = Math.random();
  }
}

Connecting to the Functionality Class

Here's an empty bitty tag that uses the data-connect attribute to connect to the default export class from the module:

page.html (In progress)
<bitty-5-0 data-connect="/v5.0.0/modules/installation-example-2.js">
  <!-- child elements go here --> 
</bitty-5-0>

<script src="/releases/bitty-5.0.0.min.js" type="module"></script>  

Creating an Interactive Element

Next up is a button element with data-send and data-receive attirubtes. Both use randomNum as their value to line up with the method from the module

button snippet
  <button data-send="randomNum" data-receive="randomNum">
    Get Random Number
  </button>  

Putting It All Together

Here's the completed HTML with the button in place:

page.html (Complete)
<bitty-5-0 data-connect="/v5.0.0/modules/installation-example-2.js">
  <button data-send="randomNumTake2" data-receive="randomNumTake2">
    Get Random Number
  </button>
</bitty-5-0>

<script src="/releases/bitty-5.0.0.min.js" type="module"></script>  

And here's the module file again to see it in context

/v5.0.0/modules/installation-example-2.js
export default class {
  randomNumTake2(_event, el) {
    el.innerHTML = Math.random();
  }
}

And here's the live result:

Next Steps

The example in the overview used a button with a data-send attribute and a separate div with a data-receive attribute. Here, we're using a single button with both data-send and data-receive. No extra work is required for most elements to both send and receive the same signal.

There are some cases (like <input type="range"> elements) where you have to add check to prevent feedback. bitty provides helper funtions including one called match to cover that. Check out the Docs sections below for details. You can also check out the Test Suite. Each test includes its source code that work as their own examples as well.

Docs - The HTML data-* API

bitty sends signals to elements when events occur. Defining the signals and the events that trigger them is done with four data-* attributes that are added to <bitty-5-0> tags and their children. The four attributes are:

data-connect

Defines the class that provides functionality to the component.

Availability: <bitty-5-0> elements: yes - child elements: no

Each <bitty-5-0> requires an external class to provide its functionality. The class can reside in one of four locations:

  1. If there is no data-connect attribute on the <bitty-5-0> component tag, then bitty looks for a window.BittyClass variable. For example:

    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <script>
        window.BittyClass = class {
          defaultRandomNum(_event, el) {
            if (el) {
              el.innerHTML = Math.random();
            }
          }
        }
      </script>
    </head>
    
    <body>
      <bitty-5-0 data-debug="true">
        <button 
          data-send="defaultRandomNum"
          data-receive="defaultRandomNum">
          Get Random Number
        </button>
      </bitty-5-0>
    
    <script src="/release/bitty-5.0.0.min.js" type="module"></script>
    </body>
    </html>
    Example

    (NOTE: the if (el) {} check is generally not necessary. It's used here because of the tests further below. The one that tests window.BittyClass throws an erroneous error without it.)

  2. If the data-connect attribute exists and its value matches a variable on the window object, then the class from that variable is used. For example:

    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <script>
        window.AltBittyClass = class {
            altClassRandomNum(_event, el) {
              el.innerHTML = Math.random();
            }
        }
      </script>
    </head>
    
    <body>
      <bitty-5-0 data-connect="AltBittyClass">
        <button 
          data-send="altClassRandomNum"
          data-receive="altClassRandomNum">
          Get Random Number
        </button>
      </bitty-5-0>
    
    <script src="/release/bitty-5.0.0.min.js" type="module"></script>
    </body>
    </html>
    Example
  3. The default export from an external module when the value of `data-connect` is the path to the module. For example:

    index.html
    <!DOCTYPE html>
    <html lang="en">
    <body>
      <bitty-5-0 data-connect="/v5.0.0/modules/data-connect-default-class.js">
        <button 
          data-send="defaultModuleClassRandomNum"
          data-receive="defaultModuleClassRandomNum">
          Get Random Number
        </button>
      </bitty-5-0>
    
    
      <script src="/release/bitty-5.0.0.min.js" type="module"></script>
    </body>
    </html>
    /v5.0.0/modules/data-connect-default-class.js
    export default class {
      defaultModuleClassRandomNum(_event, el) {
        el.innerHTML = Math.random();
      }
    }
    Example
  4. A named class from an external module when the value of `data-connect` is a path followed by a space then the name of the class to look for. For example:

    index.html
    <!DOCTYPE html>
    <html lang="en">
    <body>
      <bitty-5-0 data-connect="/v5.0.0/modules/data-connect-alt-class.js AltModuleClass">
        <button 
          data-send="altModuleClassRandomNum"
          data-receive="altModuleClassRandomNum">
          Get Random Number
        </button>
      </bitty-5-0>
    
      <script src="/release/bitty-5.0.0.min.js" type="module"></script>
    </body>
    </html>
    /v5.0.0/modules/data-connect-alt-class.js
    export class AltModuleClass {
      altModuleClassRandomNum(_event, el) {
        el.innerHTML = Math.random();
      }
    }
    Example
data-listeners

Availability: <bitty-5-0> elements: yes - child elements: no

The data-listeners attribute changes the events a component listens for. The default events are `click` and `input`. Changing it to listen to `mouseenter` looks like this:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-listeners-mouseover.js"
  data-listeners="mouseover">
  <div data-send="listenerMouseOver">
    Move Mouse Over
  </div>
  <div data-receive="listenerMouseOver">-</div>
</bitty-5-0>
/v5.0.0/modules/data-listeners-mouseover.js
export default class {
  listenerMouseOver(_event, el) {
    el.innerHTML = Math.random();
  }
}
Example
Move Mouse Over
-

Multiple listeners can be added by separating them with a space:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-listeners-mouseover-out.js"
  data-listeners="mouseover mouseout">
  <div data-send="listenerMouseOverAndOut">
    Move Mouse Over
  </div>
  <div data-receive="listenerMouseOverAndOut">-</div>
</bitty-5-0>
/v5.0.0/modules/data-listeners-mouseover-out.js
export default class {
  listenerMouseOverAndOut(event, el) {
    el.innerHTML = event.type;
  }
}
Example
Move Mouse Over
-

Using `data-listeners` remove the default `click` and `input` listeners. They can be added back in explicitly if needed.

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-listeners-with-click-input.js"
  data-listeners="mouseover mouseout click input">
  <div data-send="listenerWithClickInput">
    Move Mouse Over or Click
  </div>
  <div data-receive="listenerWithClickInput">-</div>
</bitty-5-0>
/v5.0.0/modules/data-listeners-with-click-input.js
export default class {
  listenerWithClickInput(event, el) {
    el.innerHTML = event.type;
  }
}
Example
Move Mouse Over or Click
-
data-receive

Availability: bitty-5-0 elements: no - child elements: yes

Adding a `data-receive` attribute to an element sets it up to receive signals with the given name.

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-receive/single.on.js">
  <button data-send="receiveSignal">
    Click For Random Number
  </button>
  <div data-receive="receiveSignal">-</div>
</bitty-5-0>
/v5.0.0/modules/data-receive/single.on.js
export default class {
  receiveSignal(_event, el) {
    el.innerHTML = Math.random();
  }
}
Example
-

Multiple elements can receive the same signal. Each one gets passed through the corresponding function individually (i.e. a different random number is generated for each):

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-receive/multiple-elements.on.js">
  <button data-send="multipleElementSignal">
    Click For Random Number
  </button>
  <div data-receive="multipleElementSignal">-</div>
  <div data-receive="multipleElementSignal">-</div>
  <div data-receive="multipleElementSignal">-</div>
</bitty-5-0>
/v5.0.0/modules/data-receive/multiple-elements.on.js
export default class {
  multipleElementSignal(_event, el) {
    el.innerHTML = Math.random();
  }
}
Example
-
-
-

A single element can receive multiple signals by separating the signal names with a space in the `data-receive` attribute:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-receive/receive-multiple.on.js">
  <button data-send="receiveAlfa">
    Send Alfa
  </button>
  <button data-send="receiveBravo">
    Send Bravo
  </button>
  <button data-send="receiveCharlie">
    Send Charlie
  </button>
  <div data-receive="receiveAlfa receiveBravo receiveCharlie">-</div>
</bitty-5-0>
/v5.0.0/modules/data-receive/receive-multiple.on.js
export default class {
  receiveAlfa(_event, el) {
    el.innerHTML = "Got Alfa";
  }
  receiveBravo(_event, el) {
    el.innerHTML = "Got Bravo";
  }
  receiveCharlie(_event, el) {
    el.innerHTML = "Got Charlie";
  }
}
Example
-
data-send

Availability: bitty-5-0 elements: yes - child elements: yes

Sending Single Signals

The `data-send` attribute defines which signals an element sends when an event that's being listend for fires.

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/single.on.js">
  <button data-send="singleSignal">
    Click For Random Number
  </button>
  <div data-receive="singleSignal">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/single.on.js
export default class {
  singleSignal(_event, el) {
    el.innerHTML = Math.random();
  }
}
Example
-

Sending Multiple Signals

A single element can send multiple signals by separating them with a space in the `data-send` attribute:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/multiple.on.js">
  <button data-send="sendMultipleAlfa sendMultipleBravo sendMultipleCharlie">
    Send Multiple Signals
  </button>
  <div data-receive="sendMultipleAlfa">-</div>
  <div data-receive="sendMultipleBravo">-</div>
  <div data-receive="sendMultipleCharlie">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/multiple.on.js
export default class {
  sendMultipleAlfa(_event, el) {
    el.innerHTML = `Alfa ${Math.random()}`;
  }
  sendMultipleBravo(_event, el) {
    el.innerHTML = `Bravo ${Math.random()}`;
  }
  sendMultipleCharlie(_event, el) {
    el.innerHTML = `Charlie ${Math.random()}`;
  }
}
Example
-
-
-

Sending From the <bitty-5-0> tag

A `data-send` attribute can be added to the bitty-5-0 component tag itself. It create a custom `bittytagdatasend` event that only fires once after the component has been initialized:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/from-bitty.on.js"
  data-send="sendFromBitty"
>
  <div data-receive="sendFromBitty">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/from-bitty.on.js
export default class {
  sendFromBitty(event, el) {
    el.innerHTML = `Got: ${event.type}`;
  }
}
Example
-

Sending Without Receivers

If an event triggers a `data-send` signal and there are no matching `data-receive` elements, then the corresponding method is fired once with a `null` value for the element. For example:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/no-receivers.on.js">
  <button data-send="noReceivers">
    Increment
  </button>
  <div class="no-receiver-output">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/no-receivers.on.js
export default class {
  #count = 0;

  noReceivers(_event, _el) {
    this.#count += 1;
    const outputEl = document.querySelector(
      ".no-receiver-output"
    );
    outputEl.innerHTML = this.#count;
  }
}
Example
-

This is useful when the number of times an update needs to occur differers from the number of receiving elements. For example, incrementing by one works if a button is connected to only one element with a matching `data-receive` attribute:

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/one-receiver.on.js">
  <button data-send="incrementWithOneReceiver">
    Increment
  </button>
  <div data-receive="incrementWithOneReceiver">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/one-receiver.on.js
export default class {
  #count = 0;
  incrementWithOneReceiver(_event, el) {
    this.#count += 1;
    el.innerHTML = this.#count;
  }
}
Example
-

If there are multiple receivers, each one increments the `#count` on its own. For example, every click of this button adds one to each output so they end up with `1, 2, 3`, then `4, 5, 6`, etc...

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/multiple-receivers.on.js">
  <button data-send="incrementWithMultipleReceivers">
    Increment
  </button>
  <div data-receive="incrementWithMultipleReceivers">-</div>
  <div data-receive="incrementWithMultipleReceivers">-</div>
  <div data-receive="incrementWithMultipleReceivers">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/multiple-receivers.on.js
export default class {
  #count = 0;
  incrementWithMultipleReceivers(_event, el) {
    this.#count += 1;
    el.innerHTML = this.#count;
  }
}
Example
-
-
-

Sending an individual signal to do the incrementing that isn't associated with an `data-receive` elements followed by a signal that _is_ connected to them keeps them in sync

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/synced-receivers.on.js">
  <button data-send="incrementWithoutReciever displayMultipleReceivers">
    Increment
  </button>
  <div data-receive="displayMultipleReceivers">-</div>
  <div data-receive="displayMultipleReceivers">-</div>
  <div data-receive="displayMultipleReceivers">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/synced-receivers.on.js
export default class {
  #count = 0;
  incrementWithoutReciever(_event, _el) {
    this.#count += 1;
  }

  displayMultipleReceivers(_event, el) {
    el.innerHTML = this.#count;
  }
}
Example
-
-
-

async Signal Methods

Signal Methods can be `async`

page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/data-send/async-send.on.js">
  <button data-send="asyncSendDemo">
    Send Signal
  </button>
  <div data-receive="asyncSendDemo">-</div>
</bitty-5-0>
/v5.0.0/modules/data-send/async-send.on.js
export default class {
   async asyncSendDemo(_event, el) {
    el.innerHTML = "Loading...";
    await new Promise(resolve => setTimeout(resolve, 2000));
    el.innerHTML = "Done";
  }
}
Example
-

(NOTE: There's currently not a way to do an `await` from `data-send`. That's coming in a future release.)

Docs - Signal Method API

Signals names are mapped to method names in the class that provides funcionality to the component. Check out the demo in the overview for a basic example.

The bitty element is exposed to the class providing functionality via this.api. The bitty component adds the following supplemental methods to the standard element methods and properties.

forward(event, signal)
Overview
  • calls another method to trigger its signal. The event which triggered the first signal can be sent to the second.
Parameters
  • event

    The event to forward, can be null.

  • signal

    The name of the signal to forward to (required)

Examples
this.api.forward() - Basic Forwarding
/v5.0.0/modules/forward/basic.on.js
export default class {
  forwardFirstPart(event, el) {
    el.innerHTML = "event recieved and forwarded";
    this.api.forward(event, "forwardSecondPart");
  }

  forwardSecondPart(event, el) {
    el.innerHTML = `x: ${event.offsetX} - y: ${event.offsetY}`;
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/forward/basic.on.js">
  <button data-send="forwardFirstPart">Forward Event For Coords</button>
  <div data-receive="forwardFirstPart">waiting (first)</div>
  <div data-receive="forwardSecondPart">waiting (second)</div>
</bitty-5-0>
Example
waiting (first)
waiting (second)
this.api.forward() - Forwarding a null event
/v5.0.0/modules/forward/null.on.js
export default class {
  #count = 0;

  incrementThenBranch(_event, _el) {
    this.#count += 1;
    if (this.#count % 2) {
      this.api.forward(null, "branchAlfa");
    } else {
      this.api.forward(null, "branchBravo");
    }
  }

  branchAlfa(_event, el) {
    el.innerHTML = this.#count;
  }

  branchBravo(_event, el) {
    el.innerHTML = this.#count;
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/forward/null.on.js">
  <button data-send="incrementThenBranch">Forward null Event</button>
  <div data-receive="branchAlfa">waiting (alfa)</div>
  <div data-receive="branchBravo">waiting (bravo)</div>
</bitty-5-0>
Example
waiting (alfa)
waiting (bravo)
getElement(url, subs = [], options = {})
Overview
  • Requests and external file as a string of text.

  • Any optional options key/value pairs are passed to the request.

  • If the request succeeds, any find/replace patterns from the optional subs argument are applied.

  • A template element is then created.

  • The .innerHTML value of the template is set to the string.

  • The template is cloned to produce a document fragment.

  • The .firstChild elemnet of the document fragment is extrated and return as an HTML Element in an object (i.e. { ok: ELEMENT }).

  • If there's an error the return value is at object with the bitty error (i.e. { error: BITTY_ERROR }).

Parameters
  • url

    The URL of the file to get as a string

  • subs

    An array of arrays for substitutions to make

  • options

    Options to pass to the `.fetch()` method

Examples
this.api.getElement() - Basic Element Retrieval
/v5.0.0/modules/get-element/basic.on.js
export default class {
  async getElementBasic(_event, el) {
    const url = "/v5.0.0/payloads/get-element/basic.html";
    const response = await this.api.getElement(url);
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-element/basic/index.html
<div>the quick brown fox</div>
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-element/basic.on.js">
  <button data-send="getElementBasic">Get Element</button>
  <div data-receive="getElementBasic">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getElement() - Error Handling
/v5.0.0/modules/get-element/error.on.js
export default class {
  async getElementError(_event, el) {
    const url = "/v5.0.0/payloads/get-element/intentionally-missing-file.html";
    const response = await this.api.getElement(url);
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-element/error.on.js">
  <button data-send="getElementError">Get Element</button>
  <div data-receive="getElementError">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getElement() - Passing Options to .fetch()
/v5.0.0/payloads/get-element/options/index.html
<div>the quick brown fox</div>
/v5.0.0/modules/get-element/options.on.js
export default class {
  async getElementOptions(_event, el) {
    const url = "/v5.0.0/payloads/get-element/options.html";
    const subs = [];
    const options = {
      "method": "GET"
    };
    const response = await this.api.getElement(
      url, subs, options
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-element/options.on.js">
  <button data-send="getElementOptions">Get Element</button>
  <div data-receive="getElementOptions">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getElement() - RegEx Find/Replace
/v5.0.0/modules/get-element/regex.on.js
export default class {
  async getElementRegex(_event, el) {
    const url = "/v5.0.0/payloads/get-element/regex.html";
    const subs = [
      [/SPEED/g, "fast"],
      [/KIND/g, "arctic"]
    ];
    const response = await this.api.getElement(
      url, subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-element/regex/index.html
<div>the SPEED KIND fox</div>
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-element/regex.on.js">
  <button data-send="getElementRegex">Get Element</button>
  <div data-receive="getElementRegex">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getElement() - String Find/Replace
/v5.0.0/modules/get-element/subs.on.js
export default class {
  async getElementSubs(_event, el) {
    const url = "/v5.0.0/payloads/get-element/subs.html";
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const response = await this.api.getElement(
      url, subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-element/subs/index.html
<div>the SPEED KIND fox</div>
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-element/subs.on.js">
  <button data-send="getElementSubs">Get Element</button>
  <div data-receive="getElementSubs">waiting</div>
</bitty-5-0>
Example
waiting
getHTML(url, subs = [], options = {})
Overview
  • Requests and external file as a string of text.

  • Any optional options key/value pairs are passed to the request.

  • If the request succeeds, any find/replace patterns from the optional subs argument are applied.

  • A template element is then created.

  • The .innerHTML value of the template is set to the string.

  • The template is cloned to produce a document fragment.

  • The document fragment is returned in an object with an ok key (i.e. { ok: DOCUMENT_FRAGMENT }.

  • If there's an error the return value is at object with the bitty error (i.e. { error: BITTY_ERROR }).

Parameters
  • url

    The URL of the file to get as a string

  • subs

    An array of arrays for substitutions to make

  • options

    Options to pass to the `.fetch()` method

Examples
this.api.getHTML() - Basic Document Fragment Retreival
/v5.0.0/modules/get-html/basic.on.js
export default class {
  async getHTMLBasic(_event, el) {
    const response = await this.api.getHTML(
      "/v5.0.0/payloads/get-html/basic.html"
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-html/basic.on.js">
  <button data-send="getHTMLBasic">Get HTML</button>
  <div data-receive="getHTMLBasic">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-html/basic.html
<div>the quick</div>
<div>brown fox</div>
Example
waiting
this.api.getHTML() - Error Handling
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-html/error.on.js">
  <button data-send="getHTMLError">Get HTML</button>
  <div data-receive="getHTMLError">waiting</div>
</bitty-5-0>
/v5.0.0/modules/get-html/error.on.js
export default class {
  async getHTMLError(_event, el) {
    const url = "/v5.0.0/payloads/get-html/intentionally-missing-file.html";
    const response = await this.api.getHTML(url);
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
Example
waiting
this.api.getHTML() - Passing Options to .fetch()
/v5.0.0/modules/get-html/options.on.js
export default class {
  async getHTMLOptions(_event, el) {
    const url =  "/v5.0.0/payloads/get-html/options.html";
    const subs = [];
    const options = {
      "method": "GET"
    };
    const response = await this.api.getHTML(
      url, subs, options
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-html/options.on.js">
  <button data-send="getHTMLOptions">Get HTML</button>
  <div data-receive="getHTMLOptions">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getHTML() - RegEx Find/Replace
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-html/regex.on.js">
  <button data-send="getHTMLRegEx">Get HTML</button>
  <div data-receive="getHTMLRegEx">waiting</div>
</bitty-5-0>
/v5.0.0/modules/get-html/regex.on.js
export default class {
  async getHTMLRegEx(_event, el) {
    const subs = [
      [/SPEED/g, "fast"],
      [/KIND/g, "arctic"]
    ];
    const response = await this.api.getHTML(
      "/v5.0.0/payloads/get-html/regex.html",
      subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-html/regex.html
<div>the SPEED</div>
<div>KIND fox</div>
Example
waiting
this.api.getHTML() - String Find/Replace
/v5.0.0/modules/get-html/subs.on.js
export default class {
  async getHTMLSubs(_event, el) {
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const response = await this.api.getHTML(
      "/v5.0.0/payloads/get-html/subs.html",
      subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-html/subs.on.js">
  <button data-send="getHTMLSubs">Get HTML</button>
  <div data-receive="getHTMLSubs">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-html/subs.html
<div>the SPEED</div>
<div>KIND fox</div>
Example
waiting
getJSON(url, subs = [], options = {})
Overview
  • Requests and external file as a string of text.

  • Any optional options key/value pairs are passed to the request.

  • If the request succeeds, any find/replace patterns from the optional subs argument are applied.

  • The string is parsed with JSON.parse()

  • If the parsing succeeds, the resulting object is returned as the value of an ok key in a response object (i.e. { ok: OBJECT_FROM_JSON }).

  • If there's an error retrieving the file are parsing the JSON a bittyError is returned as the value of an error key in the object that's returned: (i.e. { error: BITTY_ERROR }).

Parameters
  • url

  • subs

  • options

Examples
this.api.getJSON() - Basic JSON Retrevial
/v5.0.0/modules/get-json/basic.on.js
export default class {
  async getBasicJSON(_event, el) {
    const url = "/v5.0.0/payloads/get-json/basic.on.json";
    const response = await this.api.getJSON(
      url
    );
    if (response.value) {
      el.innerHTML = response.value.text;
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-json/basic.on.json
{
  "text": "The quick brown fox"
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-json/basic.on.js">
  <button data-send="getBasicJSON">Get JSON</button>
  <div data-receive="getBasicJSON">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getJSON() - Error Handling
/v5.0.0/modules/get-json/error.on.js
export default class {
  async getErrorJSON(_event, el) {
    const url =  "/v5.0.0/payloads/get-json/intentionally-missing-file.on.json";
    const response = await this.api.getJSON(
      url
    );
    if (response.value) {
      el.innerHTML = response.value.text;
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-json/error.on.js">
  <button data-send="getErrorJSON">Get JSON</button>
  <div data-receive="getErrorJSON">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getJSON() - Passing Options to .fetch()
/v5.0.0/modules/get-json/options.on.js
export default class {
  async getJSONOptions(_event, el) {
    const url = "/v5.0.0/payloads/get-json/options.on.json";
    const subs = [];
    const options = {
      method: "GET",
    };
    const response = await this.api.getJSON(
      url, subs, options
    );
    if (response.value) {
      el.replaceChildren(response.value.text);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-json/options.on.js">
  <button data-send="getJSONOptions">Get JSON</button>
  <div data-receive="getJSONOptions">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getJSON() - RegEx Find/Replace
/v5.0.0/modules/get-json/regex.on.js
export default class {
  async getRegExJSON(_event, el) {
    const url = "/v5.0.0/payloads/get-json/regex.on.json";
    const subs = [
      [/SPEED/g, "fast"],
      [/KIND/g, "atctic"]
    ];
    const response = await this.api.getJSON(
      url, subs
    );
    if (response.value) {
      el.innerHTML = response.value.text;
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-json/regex.on.json
{
  "text": "The SPEED KIND fox"
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-json/regex.on.js">
  <button data-send="getRegExJSON">Get JSON</button>
  <div data-receive="getRegExJSON">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getJSON() - String Find/Replace
/v5.0.0/modules/get-json/subs.on.js
export default class {
  async getSubsJSON(_event, el) {
    const url = "/v5.0.0/payloads/get-json/subs.on.json";
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const response = await this.api.getJSON(
      url, subs
    );
    if (response.value) {
      el.innerHTML = response.value.text;
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-json/subs.on.json
{
  "text": "The SPEED KIND fox"
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-json/subs.on.js">
  <button data-send="getSubsJSON">Get JSON</button>
  <div data-receive="getSubsJSON">waiting</div>
</bitty-5-0>
Example
waiting
getSVG(url, subs = [], options = {})
Overview
  • Requests and external file as a string of text.

  • Any optional options key/value pairs are passed to the request.

  • If the request succeeds, any find/replace patterns from the optional subs argument are applied.

  • A template element is then created.

  • The .innerHTML value of the template is set to the string.

  • The template is cloned to produce a document fragment.

  • The svg elemnet of the document fragment is extrated and return as an HTML Element in an object (i.e. { ok: ELEMENT }).

  • If there's an error the return value is at object with the bitty error (i.e. { error: BITTY_ERROR }).

Parameters
  • url

    The URL of the file to get as a string

  • subs

    An array of arrays for substitutions to make

  • options

    Options to pass to the `.fetch()` method

Examples
this.api.getSVG() - Basic SVG Retrevial
/v5.0.0/modules/get-svg/basic.on.js
export default class {
  async getSVGBasic(_event, el) {
    const url = "/v5.0.0/payloads/get-svg/basic.svg";
    const response = await this.api.getSVG(url);
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<style>
  [data-receive=getSVGBasic] svg {
    height: 80px;
    width: auto;
  }
</style>

<bitty-5-0 data-connect="/v5.0.0/modules/get-svg/basic.on.js">
  <button data-send="getSVGBasic">Get SVG</button>
  <div data-receive="getSVGBasic">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getSVG() - Error Handling
/v5.0.0/modules/get-svg/error.on.js
export default class {
  async getSVGError(_event, el) {
    const url = "/v5.0.0/payloads/get-svg/intentionally-missing-file.svg";
    const response = await this.api.getSVG(url);
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<style>
  [data-receive=getSVGError] svg {
    height: 80px;
    width: auto;
  }
</style>

<bitty-5-0 data-connect="/v5.0.0/modules/get-svg/error.on.js">
  <button data-send="getSVGError">Get SVG</button>
  <div data-receive="getSVGError">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getSVG() - Passing Options to .fetch()
/v5.0.0/modules/get-svg/options.on.js
export default class {
  async getSVGOptions(_event, el) {
    const url = "/v5.0.0/payloads/get-svg/options.svg";
    const subs = [];
    const options = {
      method: "GET",
    };
    const response = await this.api.getSVG(
      url, subs, options
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<style>
  [data-receive=getSVGOptions] svg {
    height: 80px;
    width: auto;
  }
</style>

<bitty-5-0 data-connect="/v5.0.0/modules/get-svg/options.on.js">
  <button data-send="getSVGOptions">Get SVG</button>
  <div data-receive="getSVGOptions">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getSVG() - RegEx Find/Replace
/v5.0.0/modules/get-svg/regex.on.js
export default class {
  async getSVGRegEx(_event, el) {
    const url = "/v5.0.0/payloads/get-svg/regex.svg";
    const subs = [
      [/SPEED/g, "fast"],
      [/KIND/g, "arctic"]
    ];
    const response = await this.api.getSVG(
      url, subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-svg/regex.svg
<svg xmlns="http://www.w3.org/2000/svg" width="350pt" height="60pt" version="1.1" viewBox="0 0 350 60">
  <title>SVG RegEx Example</title>
  <g opacity="1.000" stroke="white">
    <text x="20" y="35" class="svg-red">The SPEED KIND fox</text>
  </g>
</svg>
page.html
<style>
  [data-receive=getSVGRegEx] svg {
    height: 80px;
    width: auto;
  }
</style>

<bitty-5-0 data-connect="/v5.0.0/modules/get-svg/regex.on.js">
  <button data-send="getSVGRegEx">Get SVG</button>
  <div data-receive="getSVGRegEx">waiting</div>
</bitty-5-0>
Example
waiting
this.api.getSVG() - String Find/Replace
/v5.0.0/modules/get-svg/subs.on.js
export default class {
  async getSVGSubs(_event, el) {
    const url = "/v5.0.0/payloads/get-svg/subs.svg";
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const response = await this.api.getSVG(
      url, subs
    );
    if (response.value) {
      el.replaceChildren(response.value);
    } else {
      el.innerHTML = response.error;
    }
  }
}
/v5.0.0/payloads/get-svg/subs.svg
<svg xmlns="http://www.w3.org/2000/svg" width="350pt" height="60pt" version="1.1" viewBox="0 0 350 60">
  <title>SVG Sub Example</title>
  <g opacity="1.000" stroke="red">
    <text x="20" y="35" class="svg-red">The SPEED KIND fox</text>
  </g>
</svg>
page.html
<style>
  [data-receive=getSVGSubs] svg {
    height: 80px;
    width: auto;
  }
</style>

<bitty-5-0 data-connect="/v5.0.0/modules/get-svg/subs.on.js">
  <button data-send="getSVGSubs">Get SVG</button>
  <div data-receive="getSVGSubs">waiting</div>
</bitty-5-0>
Example
waiting
getTXT(url, subs = [], options = {}, incomingMethod = "getTXT")
Overview
  • Retreives an external file as a string of text

  • Performs find and replace updates from the subs argument

  • Returns the updated content as a string

Parameters
  • url

  • subs

  • options

  • incomingMethod

    The method that's using getTXT so it can be displayed in errors if they occur

Examples
this.api.getTXT() - Basic TXT Retrieval
/v5.0.0/modules/get-txt/basic.on.js
export default class {
  async getTXTBasic(_event, el) {
    const url = "/v5.0.0/payloads/get-txt/basic.txt";
    const response = await this.api.getTXT(url);
    if (response.value) {
      el.innerHTML = response.value;
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-txt/basic.on.js">
  <button data-send="getTXTBasic">Get TXT</button>
  <div data-receive="getTXTBasic">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-txt/basic.txt
The quick brown fox
Example
waiting
this.api.getTXT() - Error Handling
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-txt/error.on.js">
  <button data-send="getTXTError">Get TXT</button>
  <div data-receive="getTXTError">waiting</div>
</bitty-5-0>
/v5.0.0/modules/get-txt/error.on.js
export default class {
  async getTXTError(_event, el) {
    const url = "/v5.0.0/payloads/get-txt/intentionally-missing-file.txt";
    const response = await this.api.getTXT(url);
    if (response.value) {
      el.innerHTML = response.value;
    } else {
      el.innerHTML = response.error;
    }
  }
}
Example
waiting
this.api.getTXT() - Passing Options to .fetch()
/v5.0.0/modules/get-txt/options.on.js
export default class {
  async getTXTOptions(_event, el) {
    const url = "/v5.0.0/payloads/get-txt/options.txt";
    const subs = [];
    const options = {
      method: "GET",
    };
    const response = await this.api.getTXT(
      url, subs, options
    );
    if (response.value) {
      el.innerHTML = response.value;
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-txt/options.on.js">
  <button data-send="getTXTOptions">Get TXT</button>
  <div data-receive="getTXTOptions">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-txt/options.txt
the quick brown fox
Example
waiting
this.api.getTXT() - RegEx Find/Replace
/v5.0.0/modules/get-txt/regex.on.js
export default class {
  async getTXTRegEx(_event, el) {
    const url = "/v5.0.0/payloads/get-txt/regex.txt";
    const subs = [
      [/SPEED/g, "fast"],
      [/KIND/g, "atctic"]
    ];
    const options = {};
    const response = await this.api.getTXT(
      url, subs, options
    );
    if (response.value) {
      el.innerHTML = response.value;
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-txt/regex.on.js">
  <button data-send="getTXTRegEx">Get TXT</button>
  <div data-receive="getTXTRegEx">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-txt/regex.txt
The SPEED KIND fox
Example
waiting
this.api.getTXT() - String Find/Replace
/v5.0.0/modules/get-txt/subs.on.js
export default class {
  async getTXTSubs(_event, el) {
    const url = "/v5.0.0/payloads/get-txt/subs.txt";
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const options = {};
    const response = await this.api.getTXT(
      url, subs, options
    );
    if (response.value) {
      el.innerHTML = response.value;
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/get-txt/subs.on.js">
  <button data-send="getTXTSubs">Get TXT</button>
  <div data-receive="getTXTSubs">waiting</div>
</bitty-5-0>
/v5.0.0/payloads/get-txt/subs.txt
The SPEED KIND fox
Example
waiting
loadCSS(url, subs, options)
Overview
  • Requests and external file as a string of text.

  • Any optional options key/value pairs are passed to the request.

  • If the request succeeds, any find/replace patterns from the optional subs argument are applied.

  • A new CSSStyleSheet is created with the contents of the text added to it via .replaceSync()

  • The stylesheet is added to the page with document.adoptedStyleSheets.push()

  • If there are no issues, the text value is returned in { ok: TEXT_STRING }.

  • If there's an error the return value is at object with the bitty error (i.e. { error: BITTY_ERROR }).

Parameters
  • url

    The URL of the file to get as a string

  • subs

    An array of arrays for substitutions to make

  • options

    Options to pass to the `.fetch()` method

Examples
this.api.loadCSS() - Basic CSS Loading
/v5.0.0/modules/load-css/basic.on.js
export default class {
  async loadCSSBasic(_event, el) {
    const url = "/v5.0.0/payloads/load-css/basic.css";
    const response = await this.api.loadCSS(url);
    if (response.value) {
      el.innerHTML = "CSS Loaded";
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/load-css/basic.on.js">
  <button data-send="loadCSSBasic" data-receive="loadCSSBasic">
    Load CSS
  </div>
</bitty-5-0>
/v5.0.0/payloads/load-css/basic.css
[data-receive=loadCSSBasic] {
  color: red;
}
Example
this.api.loadCSS() - Error Handling
/v5.0.0/modules/load-css/error.on.js
export default class {
  async loadCSSError(_event, el) {
    const url = "/v5.0.0/payloads/load-css/intentionall-missing-file.css";
    const response = await this.api.loadCSS(url);
    if (response.value) {
      el.innerHTML = "CSS Loaded";
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/load-css/error.on.js">
  <button data-send="loadCSSError" data-receive="loadCSSError">
    Load CSS
  </div>
</bitty-5-0>
Example
this.api.loadCSS() - Passing Options to .fetch()
/v5.0.0/modules/load-css/options.on.js
export default class {
  async loadCSSOptions(_event, el) {
    const url = "/v5.0.0/payloads/load-css/options.css";
    const subs = [];
    const options = {
      "method": "GET"
    };
    const response = await this.api.loadCSS(
      url, subs, options
    );
    if (response.value) {
      el.innerHTML = "CSS Loaded";
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 
    data-connect="/v5.0.0/modules/load-css/options.on.js">
    <button 
      data-send="loadCSSOptions"
      data-receive="loadCSSOptions"
    >Load CSS</button>
</bitty-5-0>
Example
this.api.loadCSS() - RegEx Find/Replace
/v5.0.0/modules/load-css/regex.on.js
export default class {
  async loadCSSRegEx(_event, el) {
    const url = "/v5.0.0/payloads/load-css/regex.css";
    const subs = [
      [/COLOR_NAME/g, "red"],
    ];
    const response = await this.api.loadCSS(
      url, subs
    );
    if (response.value) {
      el.innerHTML = "CSS Loaded";
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/load-css/regex.on.js">
  <button
    data-send="loadCSSRegEx"
    data-receive="loadCSSRegEx"
  >Load CSS</div>
</bitty-5-0>
/v5.0.0/payloads/load-css/regex.css
[data-receive=loadCSSRegEx] {
  color: COLOR_NAME;
}
Example
this.api.loadCSS() - String Find/Replace
/v5.0.0/modules/load-css/subs.on.js
export default class {
  async loadCSSSubs(_event, el) {
    const url = "/v5.0.0/payloads/load-css/subs.css";
    const subs = [
      ["COLOR_NAME", "red"]
    ];
    const response = await this.api.loadCSS(
      url, subs
    );
    if (response.value) {
      el.innerHTML = "CSS Loaded";
    } else {
      el.innerHTML = response.error;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/load-css/subs.on.js">
  <button data-send="loadCSSSubs" data-receive="loadCSSSubs">
    Load CSS
  </div>
</bitty-5-0>
/v5.0.0/payloads/load-css/subs.css
[data-receive=loadCSSSubs] {
  color: COLOR_NAME;
}
Example
makeElement(template, subs = [])
Overview
  • NOTE: This is being renamed from makeElement to makeEl in v3.x.x

  • Produces an HTML element from a string of text.

  • Performs find/replace updates from the subs parameter on the content string.

  • Creates a template and sets its .innerHTML to the value of the updated content string.

  • Clones the template and gets its .firstChild which is the returned as an element.

    NOTE: Only one element is retured. If the text snippet has more than one elemnet as sibling at the top level only the first one is returned.

Parameters
  • template

    The template string to use to make the element

  • subs

Examples
this.api.makeElement() - Make an HTML from a string
/v5.0.0/modules/make-element/basic.on.js
export default class {
  makeElementBasic(_evnet, el) {
    const html = `<div>The quick brown fox</div>`;
    const newEl = this.api.makeElement(html);
    newEl.classList.add("green");
    el.replaceChildren(newEl);
  }
}
page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/make-element/basic.on.js">
  <button data-send="makeElementBasic">Make Element</button>
  <div data-receive="makeElementBasic">waiting</div>
</bitty-5-0>
Example
waiting
this.api.makeElement() - Make an HTML Element from a string with RegEx Find/Replace
/v5.0.0/modules/make-element/regex.on.js
export default class {
  makeElementRegEx(_evnet, el) {
    const html = `<div>The 0000 KIND fox</div>`;
    const subs = [
      [/\d+/g, "fast"],
      [/KIND/g, "arctic"]
    ];
    const newEl = this.api.makeElement(html, subs);
    newEl.classList.add("green");
    el.replaceChildren(newEl);
  }
}
page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/make-element/regex.on.js">
  <button data-send="makeElementRegEx">Make Element</button>
  <div data-receive="makeElementRegEx">waiting</div>
</bitty-5-0>
Example
waiting
this.api.makeElement() - Make an HTML Element from a string with string based Find/Replace
/v5.0.0/modules/make-element/subs.on.js
export default class {
  makeElementSubs(_evnet, el) {
    const html = `<div>The SPEED KIND fox</div>`;
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const newEl = this.api.makeElement(html, subs);
    newEl.classList.add("green");
    el.replaceChildren(newEl);
  }
}
page.html
<bitty-5-0 
  data-connect="/v5.0.0/modules/make-element/subs.on.js">
  <button data-send="makeElementSubs">Make Element</button>
  <div data-receive="makeElementSubs">waiting</div>
</bitty-5-0>
Example
waiting
makeHTML(template, subs = [])
Overview
  • Performs find/replace updates from the subs parameter on the content string.

  • Creates a template and sets its .innerHTML to the value of the updated content string.

  • Clones the template and returns the result as a document fragment.

Parameters
  • template

    The template string to use to make the fragment

  • subs

Examples
this.api.makeHTML() - Make a fragment from a string
/v5.0.0/modules/make-html/basic.on.js
export default class {
  makeHTMLBasic(_event, el) {
    const html = `<div>The quick brown fox</div>`;
    const content = this.api.makeHTML(html);
    el.replaceChildren(content);
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/make-html/basic.on.js">
  <button data-send="makeHTMLBasic">Make HTML</button>
  <div data-receive="makeHTMLBasic">waiting</div>
</bitty-5-0>
Example
waiting
this.api.makeHTML() - Make a fragment from a string with RegEx Find/Replace
/v5.0.0/modules/make-html/regex.on.js
export default class {
  makeHTMLRegEx(_event, el) {
    const html = `<div>The 0000 KIND fox</div>`;
    const subs = [
      [/\d+/g, "fast"],
      [/KIND/g, "arctic"]
    ];
    const content = this.api.makeHTML(
      html, subs
    );
    el.replaceChildren(content);
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/make-html/regex.on.js">
  <button data-send="makeHTMLRegEx">Make HTML</button>
  <div data-receive="makeHTMLRegEx">waiting</div>
</bitty-5-0>
Example
waiting
this.api.makeHTML() - Make a fragment from a string with string Find/Replace
/v5.0.0/modules/make-html/subs.on.js
export default class {
  makeHTMLSubs(_event, el) {
    const html = `<div>The SPEED KIND fox</div>`;
    const subs = [
      ["SPEED", "slow"],
      ["KIND", "red"]
    ];
    const content = this.api.makeHTML(
      html, subs
    );
    el.replaceChildren(content);
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/make-html/subs.on.js">
  <button data-send="makeHTMLSubs">Make HTML</button>
  <div data-receive="makeHTMLSubs">waiting</div>
</bitty-5-0>
Example
waiting
match(event, el, key = null)
Overview
  • Compares the .target from an event and an element to see if they match on a given dataset/data-* key.

  • If no key is provided the automatically generated uuid is used.

  • The return value is true if there is a match and false if there isn't

Parameters
  • event

  • el

  • key

Examples
this.api.match() - Match on data-uuid
/v5.0.0/modules/match/uuid.on.js
export default class {
  matchWithUUID(event, el) {
    if (this.api.match(event, el)) {
      el.innerHTML = "CLICKED";
    } else {
      el.innerHTML = "-------";
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/match/uuid.on.js">
  <button data-send="matchWithUUID" data-receive="matchWithUUID">
    Check
  </button>
  <button data-send="matchWithUUID" data-receive="matchWithUUID">
    Check
  </button>
  <button data-send="matchWithUUID" data-receive="matchWithUUID">
    Check
  </button>
</bitty-5-0>
Example
this.api.match() - Match on data-KEY
/v5.0.0/modules/match/with-key.on.js
export default class {
  matchWithKey(event, el) {
    const key = "extra";
    if(this.api.match(event, el, key)) {
      el.innerHTML = `Matched: ${el.dataset.extra}`;
    } else {
      el.innerHTML = `Did Not Match: ${el.dataset.extra}`;
    }
  }
}
page.html
<bitty-5-0 data-connect="/v5.0.0/modules/match/with-key.on.js">
  <div data-extra="alfa" data-receive="matchWithKey">waiting</div> 
  <div data-extra="bravo" data-receive="matchWithKey">waiting</div> 
  <div data-extra="alfa" data-receive="matchWithKey">waiting</div> 
  <button data-extra="alfa" data-send="matchWithKey">Click To Compare</button>
</bitty-5-0>
Example
waiting
waiting
waiting
Docs - Misc.
Initialization Steps

When a web component (like bitty) is attached to a page's DOM it fires a `connectedCallback` event. bitty uses the event to do the following notable steps in this order:

  • load supplemental function into `this.api.fn`.
  • automatically generate `data-bittyid` attributes with unique IDs for the bitty element and all children elements if they don't already exist.
  • Attempt to make a connection to a supporting class either on the page or in an external module file. If the connection can't be made, an error is thrown and the process stops. Otherwise, the following steps are taken:
  • Find any child elements with `data-receive` attributes and add them to the internal list of receivers.
  • Add a mutation observer that watches for changes and refreshes receivers when new elements are added or removed. New elements also receive a `data-bittyid` if they don't already have one.
  • Add event event listeners at the document level. The defaults are `click` and `input` but can be changed with a `data-listeners` attribute on the bitty component tag.
  • Check for a `bittyInit()` method in the connected class and call it if it exists. (If the method is defined with `async bittyInit()` it is called with `await`.)
  • Check for a `data-send` attribute on the `bitty` component tag. Trigger it to send its signals if it exists. The signal is set with a type of `bittytagdatasend`. The target is the bitty element itself.

bitty this proceeds to listen for events and send signals when they are triggered.

bittyInit()

Basic Example

/v5.0.0/modules/bitty-init/basic.on.js
export default class {
  #counter = 0;

  bittyInit() {
    this.#counter = 9000;
  }

  update(_event, el) {
    el.innerHTML = this.#counter;
  }
}
page.html
<bitty-5-0 
    data-connect="/v5.0.0/modules/bitty-init/basic.on.js"
    data-send="update">
  <div data-receive="update"></div>
</bitty-5-0>
Example

async Example

/v5.0.0/modules/bitty-init/async.on.js
export default class {
  #message = null;

  async bittyInit() {
    const url = "/v5.0.0/payloads/bitty-init/basic.txt";
    const response = await this.api.getTXT(url);
    if (response.value) {
      this.#message = response.value;
    } else {
      this.#message = response.error;
    }
  }

  update(_event, el) {
    el.innerHTML = this.#message;
  }
}
page.html
<bitty-5-0 
    data-connect="/v5.0.0/modules/bitty-init/async.on.js"
    data-send="update">
  <div data-receive="update"></div>
</bitty-5-0>
Example
Block Styling

An adoptedStyleSheets is added to the page that sets:

bitty-5-0 { display: block; }
Custom data-* Attributes

bitty uses native `data-*` attributes for set up. It's also designed to work with arbitrary `data-*` attributes. Specifically, the `this.api.match` helper function compares `event.target.dataset.KEY` and `el.dataset.KEY` for matches.

UUIDs for Elements

Every bitty component and all their child elements automatically receive a `data-bittyid` attribute with a UUID. They can be used any time an ID is needed. They are also the default values that are compared via `this.api.match(event, el)` if no other key is provided.

UUIDs for Events

The event listeners that bitty uses add a `.bittyid` property to each event. They can be used to prevent processing the same event multiple times in situations where that might occur.

No Events From the Bitty Component

bitty components send a one time `bittytagdatasend` with signals from a `data-send` attribute if one exists. Other than that, no events from the bitty component itself are used. This helps avoid duplication from events that would otherwise bubble and trigger twice.

bittyCatch(event)

Some events don't come from element and don't have an approachable way to add a data-send attribute to them (e.g. `.postMessage()`). These events are captured by an optional bittyCatch(event) method. The can be adjusted and used to trigger signals by using this.api.forward()

Release Notes

Version 5.0.0

Nov. 7, 2025

This is a collection of renames for clarity and to help avoid collisions with other code that might add data- attributes. No actual functionality is changed, but the renames require a version number bump.

  • Renamed getElements to getHTML for clarity. (getElement remains the same and pulls individual HTML elements vs getHTML which pulls document fragments.)

  • Renamed makeElements to makeHTML for clarity. (makeElement remains the same and makes individual HTML elements vs makeHTML which makes document fragments.)

  • Renamed response.ok to response.value for clarity and to avoid confusion with things like response.ok === false. (i.e. response.value === false takes less mental overhead)

  • Renamed data-uuid to data-bittyid to namespace and prevent collisions with anything else the wants to use data-uuid

Version: 4.0.0

Nov. 6, 2025

  • Bumping the version number. No changes since v4.0.0-rc1.

Version: 4.0.0-rc1

Oct. 30, 2025

  • Renamed this.api.getFragment to this.api.getElements (this is a breaking change so it's a major version bump)

  • Renamed this.api.makeFragment to this.api.makeElements (with the this.api.getFragment change)

  • Added bittyCatch(event) that catches any events that don't have a .target.dataset.send value. This provides a way to catch message like from .postMessage() and decided what to do with them.

  • Removed the this.api.fn set up. It's not worth the overhead compared to just using functions directly.

  • Using a space instead of a | to separate signals and class names. This mimics class="some things" from CSS and is easier to type. Each keys must be a set of characters without spaces so there's no need for an explicit |.

  • Changed listeners to be on the window to catch things like .postMessage(). TODO: Make sure that's cool to listen on the window instead of the document.

  • Changed internal use of event.target.dataset.forward key for this.api.forward to event.bitty.forward to avoid collisions on the event.target.dataset

  • Fixed bug in this.api.forward where an event that doesn't have a .target or .target.dataset gets overridden.

  • UUID are added to events that are being forwarded if they don't already exist.

  • Migrated docs site to use variables to make it easier to upgrade versions moving forward.

Version: 3.0.0

Oct. 26, 2025

This isn't a huge jump from Version 2.x, but there are breaking changes so it gets a new number. These are quality of life improvements that make it easier to get external content while providing better errors if something goes wrong.

The details:

  • Did the following renames and additions for consistent API names for getting and adding documet fragments and elements:

    • Renamed this.api.getHTML() to this.api.getFragment() which pulls in an external file and returns it as a document fragment.

    • Added this.api.getElement() which pulls in an external file and retuns it as a single HTML Element (if there's more than one node at the top of the file only the first one gets returned).

    • Renamed this.api.useTemplate() to this.api.makeFragment() which takes a string to use as a template and returns a document fragment.

    • Added this.api.makeElement() which takes a string to use as a template and returns a single HTML Element.

  • TODO: Added getCSS() which pulls an external CSS file and loads it into the document as an adopted stylesheet.

  • Added custom BittyError class for better error handling with this.api.getTHING calls that use .fetch and for JSON parsing errors.

  • When returning values from fetches return an object with either { ok: PAYLOAD }, or { error: { /* details */ } } with extra data coming down in the error object like status codes for HTML errors.

  • Added connectedMoveCallback() to prevent connectedCallback() from firing if a component is moved.

  • Moved everything in the init into the conditional check to see if the component makes a connection to a class. Previous a few things happend prior to the connection attempt. Moving them in prevents UUIDs from being added until a conneciton has been made. Shouldn't make any real difference. It just feels more natural.

  • Added and refined the test suite for increased coverage.

Version: 2.0.0-rc2

Oct. 21, 2025

  • Properly returned undefined if there's a problem pulling an external HTML from getHTML().

  • Properly returned undefined if there's a problem pulling an external JSON from getJSON().

  • Properly returned undefined if there's a problem pulling an external SVG from getSVG().

  • Properly returned undefined if there's a problem pulling an external TXT from getTXT().

Version: 2.0.0-rc1

Oct. 19.2025

  • Renamed this.api.fetchJSON() to this.api.getJSON() to match case used with JSON.stringify(), and switch from fetch to get prelude.

  • Added substitutions param to this.api.getJSON(url, subs=[]) so all .getTHING(url, subs=[]) methods have the same behavior.

  • Added options={} to this.api.getJSON() to pass options to the fetch call. The signature is this.api.getJSON(url, subs=[], options={}).

  • Added this.api.getHTML(url, subs=[], options={}) that loads an HTML file and does find replace with subs on the initial string where subs is an array of arrays with the patterns to match and replace. The patterns can be strings or regex.

  • Added this.api.getSVG(url, subs=[], options={}) that loads an SVG file and does find replace with subs which is an array of arrays with the patterns to match and replace. The patterns can be strings or regex.

  • Added this.api.getTXT(url, subs=[], options={}) that returns the text content of an external file.

  • Switched to array or arrays for find replace strings for this.api.useTemplate(templateString, subs=[]) and defaulting to an empty array so the argument doesn't have be sent which is consistent with the .getTHING(url, subs=[])

  • Added an adoptedStylesheet that sets the <bitty-x-x> tag to display: block.

  • Added this.api.match(event, el, key=""). If a key exists, it's used to check the dataset of the event and the el and returns true if they match. If no key is provided the uuid of the event and el are compared instead.

  • Allowing whitespace around data-send attributes.

  • Allowing whitespace around data-receive attributes.

  • Added .useHTML(content, subs = []) that converts a string from content into an HTML template (doing the substitutions) then returns the first element from it as an HTML element instead of a document fragment.

  • Ingesting window.bittyFunctions from the page and const functions inside the module and providing access the them via this.api.fn.FUNCTION()

  • Ingest local const functions functions from the bitty component file that get added as this.api.fn.FUNCTION()

  • Ingested functions are bound with .bind() so that their this. is the bitty component's element itself.

  • Combined this.config and this.metadata in the constructor().

Version: 1.3.0

Oct. 13, 2025

  • Releasing v1.3.0. No changes from v1.3.0-rc1.

Version: 1.3.0-rc1

Oct. 6, 2025

  • Added fetchJson(url) method which can be called with this.api.fetchJson(url). Returns the data object if everything works. Otherwise, throws an error and returns undefined.

  • Added useTemplate() to assemble a example while doing a find and replace of text

Version: 1.2.0-rc1

Oct. 6, 2025

  • When using data-connect if the value matches a class on the window object it's used. For example: data-connect="ExampleClass" connects to window.ExampleClass.

    Previously, the class had to be in a window.bittyClasses object (e.g. window.bittyClasses = { ExampleClass: class {} };

Version: 1.1.0-rc2

Oct. 6, 2025

  • Updated console error message if no class to connect to can be found.

Version: 1.1.0-rc1

Oct. 4, 2025

  • <bitty-COMPONENT_VERSION> can be called without a data-connect attribute. When it is, it looks for a window.BittyClass on the page and uses it if it's available.

  • Removed .error() method in favor of calling console.error() directly in order to get useful line numbers.

Version: 1.0.0

Oct. 4, 2025

  • Moved v1.0.0-rc5 to v1.0.0

  • Tag name is <bitty-1-0>

Version: 1.0.0-rc5

Oct. 4, 2025

  • Fixed bug with events not being passed to signal processing.

Version: 1.0.0-rc4

Oct. 1, 2025

  • Renamed <bitty-js> component tag to <bitty-MAJOR_VERSION> for semantic versioning.

  • Call bittyInit() with await if it's an async function.

  • Moved bittyInit() before the data-send calls from the component so the module can set things up before the signals start being sent.

  • Allowing this.api.forward() to be called with a null event (e.g. this.api.forward(null, "anotherFunction");). This is done to allow things like forwarding a signal from bittyInit() or any other function where there isn't an event to pass along.

  • Fixed bug where new receivers weren't being added properly when they were child nodes of new nodes being added (and the top layer didn't have a data- attr in it)

  • Added tests for this.api.querySelector() and document.querySelector()

  • Update site to use variables for version numbers to make them easier to maintain instead of having them in multiple places for different displays.

  • Made this.metadata an object and moved version into it (along with copyright and license)

Version: 1.0.0-rc3

Sept. 23, 2025

  • Fixed bug where this.api.send() would update the data-send attribute on the original element so it no longer send the same signal. The method is now called this.api.forward().

  • Removed data-watch. Any element can receive a signal from anywhere else on the page without extra overhead. The biggest negative to this approach is that you have to be a little more careful to avoid naming collisions. But, the ease of use is well worth that minor trade off.

  • Added Test Suite Results Report to site for easier viewing.

  • Applying passed and failed colors to individual tests for quicker scanning.

  • Updated tests to use unique signal names to avoid collisions.

Version: 1.0.0-rc2

Sept. 19, 2025

  • data-watch is applied to bitty-js elements directly instead of individual elements. The signals are send back down the tree when one is received via data-watch

  • Refactored multiple UUID() calls to single function to save a few characters.

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

  • Flipped module arguments from example(el, event) to example(event, el) since that lines up better with the order of send/receive. (i.e. the event is what gets sent from data-send and the el is the element from data-receive

  • Remove HTML output of error message. They're nice during debug but wouldn't want them showing up in prod. Could use a debug flag but that's more overhead than I want to add. So, message just go to the console.

Version: 0.3.0

Sept. 17, 2025

Big refactor based on experience using 0.2.0.

  • Added more generic error handing where you just send a message and it outputs to the page along with the UUID of the element that had the problem. The elements have bitty-js-error style classes on them so their display can be controlled via CSS.

  • Updated to use the name data-connect instead of data-module

  • The data-connect can point to either a module or to a class in a global bittyClasses window object.

  • Alternate module classes are selected by passing a | followed by the desired class name instead of using data-use. (e.g. data-connect="./module.js|AltClass)

  • If there's an bittyInit() function in a module it's the first thing that gets called once bitty is loaded.

  • Removed data-call. The data-send attribute is used for everything. Nothing would fire from it in prior versions if there wasn't at least one element with a data-receive with the same name. Now it calls the function once with no element if there's no associated data-receive

  • Added test suite.

  • The biggest update is changing data-b, data-c, data-r, and data-s to data-batch, data-call, data-receive, and data-send, respectively. I originally use the shorter names to reduce the text length. The increased clarity of the longer names is worth the few extra characters.

  • Remove expanded error messages. They were nice, but added a bunch of size without a significant improvement.

  • The other big change is removing the leading _ and $ characters from function names. They were originally put in place to create a naming convention that differentiated between functions that were hit with data-call and data-send. In practice, that wasn't necessary.

  • Removed the data-batch attribute. While there are some use cases where it might be nice, the extra complexity, mental overhead, and maintance aren't worth it.

  • Added Mutation Observer so data-* functionality works on elements that are added after initialization.

  • Added Mutation Observer to watch for removed elements to pull them out of the mix when they get gone.

  • Split the web site page up into individual template for sections. Much nicer to work with.

  • Updated example functions to always use (el, _event) instead of (el, _) for clarity.

  • Removed inert/include/ignore from the top level components then remove them. This was originally a way to tell parent components to ignore specific calls and signals from children. After using it a bit, I don't think the complexity is worth it. Better to just name functions an signals so they don't collide.

  • Isolated default signal travel to only go down the DOM. That is, if there are nested bitty-js tags, signals from the child tags don't propagate to the parent by default. (see data-watch for how to send signals up and to siblings)

  • Added data-watch so parents and siblings can receive signals from their children and other siblings. With this, signals can be sent up, down, and to siblings.

  • Ignore events directly from bitty-js elements (i.e. only process events from child elements). This is done to prevent data-send attributes on bitty-js elements from firing repeatedly when things inside the element send events (e.g. clicks).

  • Renamed scripts directory to modules.

  • Moved bitty source script file under bitty-versions/bitty-v0.3.0.js and copying to prod at the root of the site (i.e. /bitty-v0.3.0.js) so examples look cleaner.

  • Renamed data-bridge to data-connect for clarity.

  • Renamed this.widget.bridge to this.module.api in bitty-js

  • Added this.api.send(key, event) to send/forward events from inside modules.

  • Added Progressive Enhancement and JavaScript Data comparison examples.

  • Added/polished a bunch of other examples.

Version: 0.2.3

June 5, 2025

Lots of error handling work in this one.

  • Moved UUID generation for the bitty-js and all data-* reactive elements to the first thing in connectedCallback() to aid in error messaging.
  • Made connectedCallback() and async function to throw loading the widget module into its own funcitoun
  • Created an #errors private var to store error messages and help by ID.
  • Added the first few error messages
  • Added this.error() for outputting errors. It takes two optional arguments: an ID and an Element. The ID maps to the IDs in #errors. They're used to add detail and help message to the error output.

    The ID defaults to 0 which is an unclassified error type. The message for that ID includes a note to the developer to use an ID to classify the error. I consider it a bug if an appropriate ID doesn't exist and request an issue be open to fix it.

    The this.error() method dumps the bitty-js elemnet after the message.

    If an element was passed to this.error() it's dumped out as well.

    The error message end up being pretty long. The end up adding a bunch of lines to the source file. That's an explicit decision aimed at making bitty easier to work with.

  • Added top level debug function that uses a debug search query param from the window.location to see if it should output.

    Only thing I don't like about it is that it shows the function's line number instead of the line number from where it was called. Something to look into.

  • The debug function takes an optional element. It dumps it to the console if one comes in.
  • Renamed data-wires to data-bridge. Fits better and maps nicer to the .bridge coming back in from the support class.
  • Set up to load the default class exported from a widget module if no other class is defined (which happens with 'data-widget')
  • Moved all examples to use a default class export instead of a named class
  • Added a data-widget attribute to the bitty-js elements to allow using multiple classes from inside a single supporting .js module file.
  • bitty-component-error and bitty-element-error classes are added to the class list of elements where errors occur.

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.

  • Added uuids as data-uuid attrs to all elements with related data-* attributes. Added them to the bitty-js elements too. I was originally thinking I'd add the UUIDs on the elements internally (i.e. el.uuid instead of el.dataset.uuid).

    I decided on the attribute approach because it offers two benefits: 1. You can see the IDs in the Element tree view of developer consoles, and 2. You can address them with CSS to update styles based on the UUID. Both of those thing will go to supporting better error messages and bug hunting.

  • Mostly content updates largely focused on detailing the opening counter example.
  • Added CONTRIBUTING file.
  • Added watcher script to build site when files change.
  • Refined the initial counter example to remove the text and change the private variable to this.num to make the examples look nicer.
  • Moved the Examples section directly below the initial basic functionality overview section.
  • Added reminders all over the place to edit the template and instead of the output for HTML page (which would be overwritten. ask me how I know).
  • Started adding prettier-ignore comments to code snippets to prevent the display output from being mangled.
  • Started stripping prettier-ignore comments from the HTML output so it doesn't show in the example
  • Same goes for // deno-fmt-ignore-file in .js files
  • Added script to maintain the open open/closed states of the section details elements across page loads. (Having them close every time I made a change was maddening)
  • Moved all the example .js files into the site root. Not a fan of that in general, but it makes the examples look better since the path is shorter.

Version: 0.2.1

June 3, 2025

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

  • Made a Hello, World example that shows basic features. (Compared to the single button press from the prior first example.)
  • Showing Hello, World code as part of the example.
  • Load templates automatically.
  • Throttling via .requestAnimationFrame().
  • Renamed data-f to data-c (i.e. "call") for clarity.
  • Renamed data-prep attribute on bitty-js tag to data-call (i.e. "call") for clarity.
  • Renamed data-init attribute on bitty-js tag to data-send so it matches data-s more closely.
  • Made basic site builder for landing page.
  • Moved everything into details elements for the landing page.

Version: 0.2.0

June 3, 2025

Setting up a bunch of the basic wiring.

  • Rewrite to provide direct access to receiving elements. They are fully accessible in the send/receive functions. (As compared to the 0.1.0 approach which required explicitly defining what would happen via an update (e.g. update .innerHTML or .value).
  • Renamed data-wrapper and the target Wrapper class to data-wires and Wires. (bitty.js is the wrapper. The Wires class is how things are hooked up.).
  • Added data-call attribute parsing to bitty-js tag. It runs functions like data-c but does so prior to adding the event listeners to the element.
  • Added data-batch attribute processing to bitty-js tags. It operates like data-b but fires before event listeners are added.
  • Added data-ignore attribute to allow components to avoid calls to the named functions. (Send signals bubble up to parents of nested components by default. This provides a way to ignore them).
  • The bitty-js element looks for an .init() method in the Wires class. If it finds one it calls it during the initialization process.
  • Passing both the element and the triggering event to the send/receive functions.
  • Moved to using data-b explicitly to separate block calls from individual function calls.
  • Order of operations is: function calls, batch calls, single send calls.
  • Temporarily removed preflight check until global functionality is defined.
  • Created example of loading a template via the .init() call back to the Wires class.
  • Created example with nested components.
  • Created example with parent of child components pulling data from the children to do a calculation.
  • Created example showing parent ignoring send signals from children.
  • Created example showing on components loading a child component via a template that becomes fully reactive.

Version: 0.1.0

June 2, 2025

Getting the project started.

  • Initial prototype.

  • <bitty-js>; wraps elements to provide them with reactive capabilities.

  • Basic data-c, data-s, and data-r in place to call functions, send, and receive updates, respectively.

  • Functionality is loaded using data-wrapper to point to a module to load. The module must export a Wrapper class that gets loaded and used to provide functions and send/receive callbacks.

  • Uses string prefixes to make determinations about how to handle data through the send/receive channels (e.g. htmlSOMETHING updates .innerHTML of an element while valueSOMETHING updates the .value).

  • Defined convention for functions. data-c maps to functions in the Wrapper class that start with a _ (underscore). The data-s and data-r attributes map to functions in the Wrapper class that start with a $ (dollar sign).

  • Decided against using data-c data-s and data-r on the bitty.js tags. That would involved a lot of extra overhead in parsing to differentiate between the top level element and the content it wraps. Using data-send instead as a replacement for data-c. Others, TBD.

  • Set up data-c="batchSOMETHIGN" functionality to send a single single that gets turned into multiple signals in the Wrapper.

  • Defined convention of using .batches to look for batches. It must be a hash where the keys match the incoming request and the value is an array of functions to run.

  • Defined .bridge to allow Wrapper functions to access the parent bitty-js element.

  • Scoped event listeners to the bitty-js elements.

  • Set up data-listeners attribute on bitty-js tags to override the default listeners (which are input and click).

  • Created example that stores its own state.

  • Created example that updates an element with the same content it sent (i.e. verified feedback can be avoided).

  • Created example using data-send to load initial values.

  • Created example that sends multiple signals.

  • Created example with multiple elements receiving the same signal.

  • Created example showing how to update global CSS variables/properties.

  • Created example showing custom event listeners.

  • Created example showing how to update CSS variables/properties scoped to the content of individual bitty-js tags.

  • Examples use parent page's CSS for styling. It confirms I'm happy with the choice to use the light DOM instead of the shadow DOM.

  • Set up initial preflight check to ensure functions are in place.

Roadmap

  • TODO: Add more tests for bittyCatch()

  • TODO: Allow sending forward without having to pass a null event. That is, if there's only one argument, it's treated as the signal with a null event. So, this.api.forward("NAME") is equivalent to this.api.forward(null, "NAME")

  • TODO: Added data-s alias for data-send and data-r attribute for data-receive

  • TODO: Added makeTXT which returns a text value from a template after running substitutions over it.

  • TODO: Investigate: Added a disconnectedCallback() to clean up anything that can be removed when a component is removed.

  • TODO: Added await:signal to data-send and this.api.forward(event, "await:signal") to await async methods.

  • TODO: Set up the bitty tag to listen for signals with data-receive to allow for things like completely switching out the contents of the component without having to rely on a child element calling .parentNode.

Out of Scope

These items are out of scope for bitty:

  • Triggering signals from events on the bitty element itself. (e.g. click events). The purpose of the element is as a wrapper that provides interactivity to its child elements. Not to be a click target itself.

  • Including the History API in bitty's this.api.

  • Including a s = new State() type object to maintain state. That could be set up if folks want to do it in their classes. It won't be included in bitty directly. The data-send and data-receive attributes can send signals between components already.

History

Reference notes about the evolution of the project.

  • Any earlier prototype limited signals so they stopped at the component by default with a forwarding mechanism that allowed signals to be seen outside. That added complexity to the overall code and the mental model of working with the components themselves.

    The approach is now to send signals all the way to the document root and for each component to have it's listeners attached to the document root. That means that if two components try to use the same signal name for different purposes, they'll collide.

    Bitty's goal is to make things easier to build. Not to build complex systems. In that line, if a collision occurs the solution is to rename one of the signals. That's much lower overhead than dealing with limiting and forwarding mechanisms.

Test Suite
Gather results...
 
bittyInit is called automatically
FAILED
Test Code
ID: 0005-bitty-init-is-called
<bitty-5-0 data-connect="/v5.0.0/tests/0005-bitty-init-is-called/test.on.js">
  <div class="test">FAILED</div>
</bitty-5-0>            
export default class {
  bittyInit() {
    this.api.querySelector("div").innerHTML = "PASSED";
  }
}            
Test: Listeners added prior to bittyInit() call
FAILED
Test Code
ID: 0007-listeneres-ready
<bitty-5-0 
  data-connect="/v5.0.0/tests/0007-listeneres-ready/test.on.js">
  <div class="test" data-receive="runTest0007">FAILED</div>
</bitty-5-0 >            
export default class {
  bittyInit() {
    this.api.forward(null, "runTest0007")
  }

  runTest0007(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Basic Send and Receive
FAILED
Test Code
ID: 0010-basic-send-receive
<bitty-5-0 
  data-connect="/v5.0.0/tests/0010-basic-send-receive/test.on.js"
  data-send="init"
  >
  <div class="test" data-receive="runTest0010">FAILED</div>
  <button data-receive="init" data-send="runTest0010">Test Trigger</button>
</bitty-5-0>            
export default class {
  init(_event, el) {
    el.click();
  }

  runTest0010(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Nested Element Sending and Receiveing
FAILED
Test Code
ID: 0015-nested-send-and-receive
<bitty-5-0
  data-connect="/v5.0.0/tests/0015-nested-send-and-receive/test.on.js"
  data-send="init"
>
  <div>
    <div>
      <div class="test" data-receive="runTest0015">FAILED</div>
    </div>
  </div>
  <div>
    <div>
      <div data-receive="init" data-send="runTest0015"></div>
    </div>
  </div>
</bitty-5-0>            
export default class {
  init(_event, el) {
    el.click();
  }

  runTest0015(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Test: bitty-5-0 data-send attribute
FAILED
Test Code
ID: 0020-send-from-bitty-js
<bitty-5-0 data-connect="/v5.0.0/tests/0020-send-from-bitty-js/test.on.js" data-send="runTest0020">
  <div class="test" data-receive="runTest0020">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0020(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Send Multiple Singnals from a Single Element
FAILED
FAILED
FAILED
Test Code
ID: 0030-send-multiple-signals
<bitty-5-0
  data-connect="/v5.0.0/tests/0030-send-multiple-signals/test.on.js"
  data-send="init"
>
  <div class="test" data-receive="runTestAlfa0030">FAILED</div>
  <div class="test" data-receive="runTestBravo0030">FAILED</div>
  <div class="test" data-receive="runTestCharlie0030">FAILED</div>

  <button
    data-receive="init"
    data-send="runTestAlfa0030 runTestBravo0030 runTestCharlie0030"
  >
    Test Trigger
  </button>
</bitty-5-0>            
export default class {
  init(_event, el) {
    el.click();
  }
  runTestAlfa0030(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestBravo0030(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestCharlie0030(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Test: Newline can separate as well as space
FAILED
FAILED
FAILED
Test Code
ID: 0035-check-newline-split
<bitty-5-0
  data-connect="/v5.0.0/tests/0035-check-newline-split/test.on.js"
  data-send="init"
>
  <div class="test" data-receive="runTestAlfa0035">FAILED</div>
  <div class="test" data-receive="runTestBravo0035">FAILED</div>
  <div class="test" data-receive="runTestCharlie0035">FAILED</div>

  <button
    data-receive="init"
    data-send="runTestAlfa0035
runTestBravo0035
runTestCharlie0035"
  >
    Test Trigger
  </button>
</bitty-5-0>            
export default class {
  init(_event, el) {
    el.click();
  }
  runTestAlfa0035(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestBravo0035(_event, el) {
    el.innerHTML = "PASSED";
  }
  runTestCharlie0035(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Multiple Receivers for a Single Sender
FAILED
FAILED
FAILED
Test Code
ID: 0040-multiple-receivers
<bitty-5-0 data-connect="/v5.0.0/tests/0040-multiple-receivers/test.on.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-5-0>            
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-5-0 data-connect="/v5.0.0/tests/0050-accessing-data-from-sending-element/test.on.js">
  <button data-status="PASSED" data-send="runTest50">Test Trigger</button>
  <div class="test" data-receive="runTest50">FAILED</div>
</bitty-5-0>            
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-5-0 data-connect="/v5.0.0/tests/0060-access-data-from-receiving-element/test.on.js">
  <button data-send="runTest0060">Test Trigger</button>
  <div class="test" data-status="PASSED" data-receive="runTest0060">FAILED</div>
</bitty-5-0>            
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-5-0 data-connect="/v5.0.0/tests/0070-many-sends-to-many-receivers/test.on.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-5-0>            
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-5-0 data-connect="/v5.0.0/tests/0075-forward-inside-module/test.on.js">
  <button data-send="runTest0075">Test Trigger</button>
  <div class="test" data-receive="forwardEvent0075">FAILED</div>
</bitty-5-0>            
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-5-0 data-connect="/v5.0.0/tests/0080-storing-state/test.on.js">
  <button data-send="runTest0080">Test Trigger</button>
  <div class="test" data-receive="runTest0080">FAILED</div>
</bitty-5-0>            
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
FAILED
Test Code
ID: 0090-updating-self-loopback-signal
<bitty-5-0 data-connect="/v5.0.0/tests/0090-updating-self-loopback-signal/test.on.js">
  <div class="test" data-send="runTest0090" data-receive="runTest0090">FAILED</div>
</bitty-5-0>            
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("div");
    button.click();
  }
  runTest0090(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Get Event Type
FAILED
Test Code
ID: 0100-get-event-type
<bitty-5-0 data-connect="/v5.0.0/tests/0100-get-event-type/test.on.js">
  <button data-send="runTest0100">Test Trigger</button>
  <div class="test" data-receive="runTest0100">FAILED</div>
</bitty-5-0>            
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-5-0 
  data-connect="/v5.0.0/tests/0110-verify-uuids-exist-on-senders/test.on.js"
  data-send="runTest0110"
>
  <div class="test" data-receive="runTest0110">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0110(event, el) {
    if (event.target.dataset.bittyid !== undefined) {
      el.innerHTML = "PASSED";
    }
  }
}            
Verify UUIDs are Created on Receiving Elements
FAILED
Test Code
ID: 0120-verify-uuids-exist-on-receivers
<bitty-5-0 
  data-connect="/v5.0.0/tests/0120-verify-uuids-exist-on-receivers/test.on.js"
  data-send="runTest0120"
>
  <div class="test" data-receive="runTest0120">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0120(_event, element) {
    if (element.dataset.bittyid !== undefined) {
      element.innerHTML = "PASSED";
    }
  }
}            
Verify UUIDs Exist On All Elements
FAILED
Test Code
ID: 0125-uuids-exist-on-all-elements
<bitty-5-0 data-connect="/v5.0.0/tests/0125-uuids-exist-on-all-elements/test.on.js">
  <div class="test">FAILED</div>
</bitty-5-0>            
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.bittyid) {
      el.innerHTML = "PASSED";
    }
  }
}            
Verify UUIDs Are Added To Events
FAILED
Test Code
ID: 0127-uuids-exist-on-events
<bitty-5-0 
  data-connect="/v5.0.0/tests/0127-uuids-exist-on-events/test.on.js"
  data-send="runTest0127"
>
  <div class="test" data-receive="runTest0127">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0127(event, element) {
    if (event.bittyid !== undefined) {
      element.innerHTML = "PASSED";
    }
  }
}            
Existing data-uuid Attributes Aren't Overwritten
FAILED
FAILED
Test Code
ID: 0128-uuids-are-not-overwritten
<bitty-5-0 data-connect="/v5.0.0/tests/0128-uuids-are-not-overwritten/test.on.js">
  <div class="test" data-uuid="original-uuid">FAILED</div>
  <div class="test" data-receive="testStub" data-uuid="original-uuid">FAILED</div>
</bitty-5-0>            
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-5-0 
  data-connect="/v5.0.0/tests/0130-create-an-element/test.on.js"
  data-send="runTest0130"
>
  <div class="test test-element-wrapper" data-receive="runTest0130">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0130(_event, el) {
    const newDiv = document.createElement("div");
    await this.api.appendChild(newDiv);
    if (newDiv.dataset.bittyid !== undefined) {
      el.innerHTML = "PASSED";
    }
  }
}            
Ensure Elements Made from Templates Get UUIDs
FAILED
Test Code
ID: 0140-ensure-template-elements-get-uuids
<bitty-5-0 
  data-connect="/v5.0.0/tests/0140-ensure-template-elements-get-uuids/test.on.js"
  data-send="runTest0140"
>
  <div class="test-element-wrapper" data-receive="runTest0140">FAILED</div>
</bitty-5-0>            
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

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

export default class {
  async runTest0140(_event, element) {
    let newButton = template.content.cloneNode(true);
    element.replaceChildren(newButton);
    element.childNodes[0].classList.add("test");
    // sleep for test to wait for observer
    // to update the UUID.
    await sleep(100);
    if (element.childNodes[0].dataset.bittyid !== undefined) {
      element.childNodes[0].innerHTML = "PASSED";
    }
  }
}            
Verify the bittyInit() Function Explicitly
FAILED
Test Code
ID: 0160-init-function-works
<bitty-5-0 data-connect="/v5.0.0/tests/0160-init-function-works/test.on.js" class="test">FAILED</bitty-5-0>            
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-5-0 
  data-connect="/v5.0.0/tests/0170-use-data-send-without-a-receiver/test.on.js"
  data-send="runTest0170"
>
  <div class="test">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0170(event, _el) {
    this.api.querySelector("div").innerHTML = "PASSED";
  }
}            
Chain this.api.forward() calls
FAILED
FAILED
FAILED
FAILED
Test Code
ID: 0220-chain-forword-api-calls
<bitty-5-0 
    data-connect="/v5.0.0/tests/0220-chain-forword-api-calls/test.on.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-5-0>            
// 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-5-0 
    data-connect="/v5.0.0/tests/0230-forward-multiple-signals/test.on.js"
    data-send="runTest0230"
>
    <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-5-0>            
export default class {
  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";
  }
}            
Call bittyInit() with await if it's async
FAILED
Test Code
ID: 0250-async-bitty-init
<bitty-5-0 data-connect="/v5.0.0/tests/0250-async-bitty-init/test.on.js">
  <div class="test">FAILED</div>
</bitty-5-0>            
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100);
    const el = this.api.querySelector("div");
    el.innerHTML = "PASSED";
  }
}            
Use this.api.querySelector()
FAILED
Test Code
ID: 0260-use-api-query-selector
<bitty-5-0 data-connect="/v5.0.0/tests/0260-use-api-query-selector/test.on.js">
  <div id="testApiQuerySelectorTarget" class="test">FAILED</div>
</bitty-5-0>            
export default class {
  bittyInit() {
    const el = this.api.querySelector("#testApiQuerySelectorTarget");
    el.innerHTML = "PASSED";
  }
}            
Documnet query selector
FAILED
Test Code
ID: 0270-use-document-query-selector
<div id="testDocumentQuerySelectorTarget" class="test">FAILED</div>

<bitty-5-0 
    data-connect="/v5.0.0/tests/0270-use-document-query-selector/test.on.js"
    data-send="runTest0270"
    >
</bitty-5-0>            
export default class {
  runTest0270(_event, _element) {
    const el = document.querySelector("#testDocumentQuerySelectorTarget");
    el.innerHTML = "PASSED";
  }
}            
Forward a Signal Without an Event
FAILED
Test Code
ID: 0280-forward-without-event
<bitty-5-0 data-connect="/v5.0.0/tests/0280-forward-without-event/test.on.js">
    <div class="test" data-receive="runTest0280">FAILED</div>
</bitty-5-0>            
export default class {
  bittyInit() {
    this.api.forward(null, "runTest0280");
  }

  runTest0280(_event, element) {
    element.innerHTML = "PASSED";
  }
}            
Check init Order
FAILED
Test Code
ID: 0290-check-init-order
<bitty-5-0 
  data-connect="/v5.0.0/tests/0290-check-init-order/test.on.js"
  data-send="runTest0290"
>
    <div class="test" data-receive="runTest0290">FAILED</div>
</bitty-5-0>            
export default class {
  #textString = "FAILED";

  bittyInit() {
    this.#textString = "PASSED";
  }

  runTest0290(_event, el) {
    el.innerHTML = this.#textString;
  }
}            
Load New Node Receivers
FAILED
Test Code
ID: 0300-load-new-nodes
<bitty-5-0 
  data-connect="/v5.0.0/tests/0300-load-new-nodes/test.on.js"
>
  <div class="test" data-receive="runTest0300">FAILED</div>
</bitty-5-0>            
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export default class {
  async bittyInit() {
    await sleep(100);
    const newTree = document.createElement("div");
    newTree.innerHTML = `<div>
    <div class="test" data-receive="runTest0300">FAILED</div>
    </div>`;
    this.api.appendChild(newTree);
    await sleep(100);
    this.api.forward(null, "runTest0300");
  }

  runTest0300(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Load Default window.BittyClass from Page (Manual Test)
FAILED
Test Code
ID: 0330-load-default-page-class
<bitty-5-0 data-send="runTest0330">
  <div class="test" data-receive="runTest0330">FAILED</div>
</bitty-5-0>            
// This test comes from `window.BittyClass`
// on the parent HTML page.            
Load Specific Class from window.TestClass0340 (Manual Test)
FAILED
Test Code
ID: 0340-load-directly-from-page
<script>
window.TestClass0340 = class {
  runTest0340(_event, el) {
    el.innerHTML = "PASSED";
  }
}
</script>

<bitty-5-0 
  data-connect="TestClass0340"
  data-send="runTest0340"
>
  <div class="test" data-receive="runTest0340">FAILED</div>
</bitty-5-0>            
// This test comes from `window.TestClass0340`
// on the parent HTML page.            
Change Event Listeners
FAILED
Test Code
ID: 0350-change-event-listeners
<bitty-5-0 
  data-connect="/v5.0.0/tests/0350-change-event-listeners/test.on.js"
  data-listeners="altevent"
>
  <div class="test" data-receive="runTest0350">FAILED</div>
</bitty-5-0>            
export default class {
  async bittyInit() {
    this.api.addEventListener("altevent", () => {
      this.api.forward(null, "runTest0350");
    });
    const event = new Event("altevent");
    this.api.dispatchEvent(event);
  }

  runTest0350(_event, element) {
    element.innerHTML = "PASSED";
  }
}            
Nested bitty data-send does not bubble
PASSED
Test Code
ID: 0360-nested-send-does-not-bubble
<bitty-5-0 
    data-connect="/v5.0.0/tests/0360-nested-send-does-not-bubble/test.on.js Parent">
  <div class="test" data-receive="runTest0360">PASSED</div>
  <bitty-5-0 
      data-connect="/v5.0.0/tests/0360-nested-send-does-not-bubble/test.on.js Child"
      data-send="runTest0360"
  >
  </bitty-5-0>
</bitty-5-0>            
export class Parent {
  runTest0360(_event, el) {
    el.innerHTML = "FAILED";
  }
}

export class Child {
  runTest0360(_event, el) {
    if (el) {
      el.innerHTML = "FAILED";
    }
  }
}            
bitty element data-send uses bittysend event
FAILED
Test Code
ID: 0370-bittysend-event
<bitty-5-0 
  data-connect="/v5.0.0/tests/0370-bittysend-event/test.on.js"
  data-send="runTest0370"
>
  <div class="test" data-receive="runTest0370">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0370(event, el) {
    if (event.type === "bittytagdatasend") {
      el.innerHTML = "PASSED";
    }
  }
}            
Forward bitty tag data-send event
FAILED
Test Code
ID: 0380-forward-bitty-tag-send
<bitty-5-0 
  data-connect="/v5.0.0/tests/0380-forward-bitty-tag-send/test.on.js"
  data-send="runTest0380"
>
  <div class="test" data-receive="runTest0380forward">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0380(event, _el) {
    this.api.forward(event, "runTest0380forward");
  }

  runTest0380forward(event, el) {
    if (event.type = "bittytagdatasend") {
      el.innerHTML = "PASSED";
    }
  }
}            
Test: getJSON(url) - Success
FAILED
Test Code
ID: 0390-get-json
<bitty-5-0 
  data-connect="/v5.0.0/tests/0390-get-json/test.on.js"
  data-send="runTest0390"
>
  <div class="test" data-receive="runTest0390">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0390(_event, el) {
    const url = "/v5.0.0/tests/0390-get-json/payload.json";
    const response = await this.api.getJSON(url);
    if (response.value) {
      el.innerHTML = response.value.status;
    }
  }
}            
Test: getJSON(url) - Fetch Error
FAILED
Test Code
ID: 0393-get-json-fetch-error
<bitty-5-0 
  data-connect="/v5.0.0/tests/0393-get-json-fetch-error/test.on.js"
  data-send="runTest0393"
>
  <div class="test" data-receive="runTest0393">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0393(_event, el) {
    const url = "/v5.0.0/tests/0393-get-json-fetch-error/intentionally-missing-file.json";
    const response = await this.api.getJSON(url);
    if (response.error.status === 404) {
      el.innerHTML = "PASSED";
    }
  }
}            
Test: getJSON(url) - Parsing Error
FAILED
Test Code
ID: 0394-get-json-parse-error
<bitty-5-0 
  data-connect="/v5.0.0/tests/0394-get-json-parse-error/test.on.js"
  data-send="runTest0394"
>
  <div class="test" data-receive="runTest0394">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0394(_event, el) {
    const url = "/v5.0.0/tests/0394-get-json-parse-error/invalid-json.json";
    const response = await this.api.getJSON(url);
    if (response.error.type === "parsing") {
      el.innerHTML = "PASSED";
    }
  }
}            
Test: getJSON(url, subs) - Success
FAILED
Test Code
ID: 0395-get-json-with-subs
<bitty-5-0 
  data-connect="/v5.0.0/tests/0395-get-json-with-subs/test.on.js"
  data-send="runTest0395"
>
  <div class="test" data-receive="runTest0395">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0395(_event, el) {
    const url = "/v5.0.0/tests/0395-get-json-with-subs/payload.json";
    const subs = [
      [/FAILED/g, "PASSED"],
    ];
    const response = await this.api.getJSON(url, subs);
    if (response.value) {
      el.innerHTML = response.value.status;
    }
  }
}            
Test: getJSON(url, sub, options) - Success
FAILED
Test Code
ID: 0397-get-json-with-options
<bitty-5-0 
  data-connect="/v5.0.0/tests/0397-get-json-with-options/test.on.js"
  data-send="runTest0397"
>
  <div class="test" data-receive="runTest0397">FAILED</div>

</bitty-5-0>

<!--


  <details>
    <summary>NOTE</summary>
    <p>NOTE: This tests uses `https://echo.hoppscotch.io/`
      to generate the response. If there's a failure
      that's the first place to look to make sure
      the service is operational
    </p>
    <p>NOTE: all the `.fetchThing()` methods use the same 
      underlying `fetchTxt(url, subs=[], options={})` call. 
      A passing test hear implies a passing test for
      `options = {}` on `.fetchTxt()`, `.fetchHTML()`, etc...
    </p>
  </details>

-->            
export default class {
  async runTest0397(_event, el) {
    const url = "https://echo.hoppscotch.io/";
    const subs = [];
    const options = { method: "POST" };
    const response = await this.api.getJSON(url, subs, options);
    if (response.value && response.value.method === "POST") {
      el.innerHTML = "PASSED";
    }
  }
}            
Test: makeFragment(template, subs)
FAILED
Test Code
ID: 0400-make-elements
<bitty-5-0 
  data-connect="/v5.0.0/tests/0400-make-elements/test.on.js"
  data-send="runTest0400"
>
  <div class="test" data-receive="runTest0400">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0400(_event, el) {
    el.classList.remove("test");
    const template = `<div class="test">FAILED</div>`;
    const subs = [[/FAILED/g, "PASSED"]];
    el.replaceChildren(this.api.makeHTML(
      template,
      subs,
    ));
  }
}            
Test: getFragment(url) - Success
FAILED
Test Code
ID: 0410-get-html
<bitty-5-0 
  data-connect="/v5.0.0/tests/0410-get-html/test.on.js"
  data-send="runTest0410"
>
  <div class="test" data-receive="runTest0410">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0410(_event, el) {
    const url = `/v5.0.0/tests/0410-get-html/payload.html`;
    const response = await this.api.getHTML(url);
    el.parentNode.replaceChildren(response.value);
  }
}            
Test: getFragment(url) - Fetching Error
FAILED
Test Code
ID: 0415-get-html-fetching-error
<bitty-5-0 
  data-connect="/v5.0.0/tests/0415-get-html-fetching-error/test.on.js"
  data-send="runTest0415"
>
  <div class="test" data-receive="runTest0415">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0415(_event, el) {
    const url = `/v5.0.0/tests/0415-get-html-fetching-error/payload/intentionally-missing-file.html`;
    const response = await this.api.getHTML(url);
    if (response.error) {
      el.innerHTML = "PASSED";
    }
  }
}            
Fetch with this.api.fetchHTML() Using Replacements
FAILED
Test Code
ID: 0420-get-html-with-subs
<bitty-5-0 
  data-connect="/v5.0.0/tests/0420-get-html-with-subs/test.on.js"
  data-send="runTest0420"
>
  <div class="test" data-receive="runTest0420">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0420(_event, el) {
    const url = `/v5.0.0/tests/0420-get-html-with-subs/payload.html`;
    const subs = [["FAILED", "PASSED"]];
    const response = await this.api.getHTML(url, subs);
    el.parentNode.replaceChildren(response.value);
  }
}            
Fetch with this.api.fetchHTML() and RegEx Replacements
FAILED
Test Code
ID: 0430-get-html-with-regex
<bitty-5-0 
  data-connect="/v5.0.0/tests/0430-get-html-with-regex/test.on.js"
  data-send="runTest0430"
>
  <div class="test" data-receive="runTest0430">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0430(_event, el) {
    const url = `/v5.0.0/tests/0430-get-html-with-regex/payload.html`;
    const subs = [[/FAILED/g, "PASSED"]];
    const response = await this.api.getHTML(url, subs);
    el.parentNode.replaceChildren(response.value);
  }
}            
Test: getSVG(url, subs) - Success
FAILED
Test Code
ID: 0440-get-svg
<style>
.svg-test-class {
  stroke: green;
  fill: green;
}
</style>
<bitty-5-0 
  data-connect="/v5.0.0/tests/0440-get-svg/test.on.js"
  data-send="runTest0440"
>
  <div class="test" data-receive="runTest0440">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0440(_event, el) {
    const url = `/v5.0.0/tests/0440-get-svg/payload.svg`;
    const subs = [[/FAILED/g, "PASSED"]];
    const response = await this.api.getSVG(url, subs);
    el.parentNode.replaceChildren(response.value);
  }
}            
Test: getSVG(url) - Fetching Error
FAILED
Test Code
ID: 0445-get-svg-error
<bitty-5-0 
  data-connect="/v5.0.0/tests/0445-get-svg-error/test.on.js"
  data-send="runTest0445"
>
  <div class="test" data-receive="runTest0445">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0445(_event, el) {
    const url = `/v5.0.0/tests/0445-get-svg/intentionally-missing-file.svg`;
    const response = await this.api.getSVG(url);
    if (response.error) {
      el.innerHTML = "PASSED"
    };
  }
}            
Test match(event, el, KEY)
FAILED
FAILED
FAILED
FAILED
FAILED
Test Code
ID: 0460-match-data
<bitty-5-0 
  data-connect="/v5.0.0/tests/0460-match-data/test.on.js"
  data-send="
    runTest0460a
    runTest0460b
    runTest0460c
    runTest0460d 
    runTest0460e"
  data-alfa="widget"
>
  <div class="test" data-alfa="widget" data-receive="runTest0460a">FAILED</div>
  <div class="test" data-alfa="not-widget" data-receive="runTest0460b">FAILED</div>
  <div class="test" data-receive="runTest0460c">FAILED</div>
  <div class="test" data-receive="runTest0460d">FAILED</div>
  <div class="test" data-bravo="widget" data-receive="runTest0460e">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0460a(event, el) {
    if (this.api.match(event, el, "alfa") === true) {
      el.innerHTML = "PASSED";
    }
  }
  runTest0460b(event, el) {
    if (this.api.match(event, el, "alfa") === false) {
      el.innerHTML = "PASSED";
    }
  }
  runTest0460c(event, el) {
    if (this.api.match(event, el, "alfa") === false) {
      el.innerHTML = "PASSED";
    }
  }
  runTest0460d(event, el) {
    if (this.api.match(event, el, "noMatchingKey") === false) {
      el.innerHTML = "PASSED";
    }
  }
  runTest0460e(event, el) {
    if (this.api.match(event, el, "bravo") === false) {
      el.innerHTML = "PASSED";
    }
  }
}            
FAILED
Test Code
ID: 0465-match-data-click
<bitty-5-0 data-connect="/v5.0.0/tests/0465-match-data-click/test.on.js">
  <button data-send="runTest0465" data-extra="foxtrot">Test Target</button>
  <div data-receive="runTest0465" data-extra="foxtrot" class="test" >FAILED</div>
</bitty-5-0>            
export default class { 
  bittyInit() {
    this.api.querySelector("button").click();
  }

  runTest0465(event, el) {
    if (this.api.match(event, el, "extra") == true) {
      el.innerHTML = "PASSED";
    }
  }
}            
Test this.page.match(event, el) without key
FAILED
Test Code
ID: 0470-match-uuid
<bitty-5-0 
  data-connect="/v5.0.0/tests/0470-match-uuid/test.on.js"
  data-send="initTest0470"
>
  <div class="test" data-receive="runTest0470b">FAILED</div>
  <button 
    class="test" 
    data-send="runTest0470a runTest0470b"
    data-receive="initTest0470 runTest0470a" 
   >FAILED</button>
</bitty-5-0>            
export default class {
  initTest0470(event, el) {
    el.click();
  }
  runTest0470a(event, el) {
    if (this.api.match(event, el) === true) {
      el.innerHTML = "PASSED";
    }
  }
  runTest0470b(event, el) {
    if (this.api.match(event, el) === false) {
      el.innerHTML = "PASSED";
    }
  }
}            
Allow empty space around data-send and data-receive
FAILED
Test Code
ID: 0480-allow-empty-space
<bitty-5-0 
  data-connect="/v5.0.0/tests/0480-allow-empty-space/test.on.js"
  data-send="

  spacerAlfa 

  runTest0480 



  spacerBravo

  "
>
  <div class="test" data-receive="
    spacerCharlie


    runTest0480


    spacerDelta 

    ">FAILED</div>
</bitty-5-0>            
export default class {
  runTest0480(_event, el) {
    el.innerHTML = "PASSED";
  }
}            
Test: getTXT(url)
FAILED
Test Code
ID: 0490-get-txt
<bitty-5-0 
  data-connect="/v5.0.0/tests/0490-get-txt/test.on.js"
  data-send="runTest0490"
>
  <div class="test" data-receive="runTest0490">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0490(_event, el) {
    const url = "/v5.0.0/tests/0490-get-txt/payload.txt";
    const response = await this.api.getTXT(url);
    el.innerHTML = response.value;
  }
}            
Test: getTXT(url, sub, options)
FAILED
Test Code
ID: 0493-get-txt-subs
<bitty-5-0 
  data-connect="/v5.0.0/tests/0493-get-txt-subs/test.on.js"
  data-send="runTest0493"
>
  <div class="test" data-receive="runTest0493">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0493(_event, el) {
    const url = "/v5.0.0/tests/0493-get-txt-subs/payload.txt";
    const subs = [[/FAILED/g, "PASSED"]];
    const options = {};
    const response = await this.api.getTXT(url, subs, options);
    if (response.value) {
      el.innerHTML = response.value;
    }
  }
}            
Test: getTXT(url) Error
FAILED
Test Code
ID: 0495-get-txt-fetching-error
<bitty-5-0 
  data-connect="/v5.0.0/tests/0495-get-txt-fetching-error/test.on.js"
  data-send="runTest0495"
>
  <div class="test" data-receive="runTest0495">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0495(_event, el) {
    const url = "/v5.0.0/tests/0495-get-txt-fetching-error/intentionally-missing-file.txt";
    const response = await this.api.getTXT(url);
    if (response.error.type === "fetching" 
          && response.error.message !== undefined
          && response.error.status === 404
) {
      el.innerHTML = "PASSED";
    }
  }
}            
Test useEl(content, subs = [])
FAILED
Test Code
ID: 0540-make-element
<bitty-5-0 
  data-connect="/v5.0.0/tests/0540-make-element/test.on.js"
  data-send="runTest0540"
>
  <div data-receive="runTest0540">
    <div class="test">FAILED</div>
  </div>
</bitty-5-0>            
const template = `<div class="test">FAILED</div>`;

export default class {
  runTest0540(_event, el) {
    const newChild = this.api.makeElement(template);
    newChild.innerHTML = "PASSED";
    el.replaceChildren(newChild);
  }
}            
Test useEl(content, subs = []) with blankspace
FAILED
Test Code
ID: 0550-make-element-trim
<bitty-5-0 
  data-connect="/v5.0.0/tests/0550-make-element-trim/test.on.js"
  data-send="runTest0550"
>
  <div data-receive="runTest0550">
    <div class="test">FAILED</div>
  </div>
</bitty-5-0>            
const template = `

  <div class="test">FAILED</div>

`;

export default class {
  runTest0550(_event, el) {
    const newChild = this.api.makeElement(
      template,
    );
    newChild.innerHTML = "PASSED";
    el.replaceChildren(newChild);
  }
}            
Test getElement(url)
FAILED
Test Code
ID: 0560-get-element
<bitty-5-0
  data-connect="/v5.0.0/tests/0560-get-element/test.on.js"
  data-send="runTest0560"
>
  <div class="test" data-receive="runTest0560">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0560(_event, el) {
    const url = `/v5.0.0/tests/0560-get-element/payload.html`;
    const response = await this.api.getElement(url);
    if (response.value) {
      el.parentNode.replaceChildren(response.value);
    }
  }
}            
Test getElement(url, subs)
FAILED
Test Code
ID: 0570-get-element-subs
<bitty-5-0
  data-connect="/v5.0.0/tests/0570-get-element-subs/test.on.js"
  data-send="runTest0570"
>
  <div class="test" data-receive="runTest0570">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0570(_event, el) {
    const url = `/v5.0.0/tests/0570-get-element-subs/payload.html`;
    const subs = [[/FAILED/g, "PASSED"]];
    const response = await this.api.getElement(url, subs);
    el.parentNode.replaceChildren(response.value);
  }
}            
Test getElement(url) - Fetching Error
FAILED
Test Code
ID: 0580-get-element-fetching-error
<bitty-5-0
  data-connect="/v5.0.0/tests/0580-get-element-fetching-error/test.on.js"
  data-send="runTest0580"
>
  <div class="test" data-receive="runTest0580">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0580(_event, el) {
    const url =
      `/v5.0.0/tests/0580-get-element-fetching-error/intentionally-missing-file.html`;
    const response = await this.api.getElement(url);
    if (response.error.status === 404) {
      el.innerHTML = "PASSED";
    }
  }
}            
Test: loadCSS
FAILED
Test Code
ID: 0590-load-css
<bitty-5-0
  data-connect="/v5.0.0/tests/0590-load-css/test.on.js"
  data-send="runTest0590"
>
  <div class="test" data-receive="runTest0590">FAILED</div>
</bitty-5-0>            
export default class {
  async runTest0590(_event, el) {
    const url = `/v5.0.0/tests/0590-load-css/payload.css`;
    const response = await this.api.loadCSS(url);
    const styles = getComputedStyle(document.documentElement);
    const checkValue = styles.getPropertyValue('--test-0590-value');
    if (checkValue === "green") {
      el.innerHTML = "PASSED";
    }
  }
}            
License

MIT License

Copyright (c) 2025 Alan Smith - https://bitty.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.