npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

formul8

v0.5.0

Published

Automatic form handling

Readme

NAME

formul8 — Automatic form handling

SYNOPSIS

import { formul8 } from "formul8"

const [form_data, change_event] = formul8("my_form_id")

do_stuff(form_data)
document.addEventListener(change_event, () => do_stuff(form_data))

DESCRIPTION

Takes in a <form/> (or any HTML element) that contains <input/>, <select/>, and <textarea/> elements, and returns an object that represents the "state" of the form. Each of the input-like elements within the form will have an entry in the state object whose key is the input name if present, else the id if present, else a value auto-generated by formul8.

When any of the form elements change, the state object will be updated and a custom event will be fired on the document. The name of this event is the second thing returned by formul8, and the event object holds the state object in the detail field.

When the values of state object change, the form is updated to change with them and the same custom event is fired.

Any <fieldset/> elements in the form create sub-objects, where the key of the sub-object is the fieldset name is present, else the id if present, else the text of a child <legend/> if present, else a value auto generated by formul8.

OPTIONS

Additional options can be passed to formul8 through the optional parameter options, a JSON object.

DEBOUNCE

If options.debounce is a number, it overrides the default debounce period on the form (0 milliseconds).

EVENTS

If options.events is a list of strings, it becomes the list of events that the form listens for to detect changes.

DIRECTION

If options.direction is a update_dir enum value, it will define which ways the form updates. The directions are:

update_dir.from_form = 1  // Changing the form elements changes the object.
update_dir.to_form = 2    // Changing the object changes the form elements.

The default value is update_dir.from_form | update_dir.to_form, A.K.A. bidirectional.

AUTO_NOTIFY

If options.auto_notify is true and direction includes update_dir.to_form, then changing the state object automatically fires the custom change event in addition to updating the form. If you want to control this manually, set options.auto_notify to false and use notify(change_event, form_data) to manually fire the custom change event.

CUSTOM_INPUTS

Custom input elements can be defined by seting options.custom_inputs to a dictionary of CSS selectors and getter/setter pairs:

{
  "div.custom_input1": {
    get: (element: HTMLElement): any => some_getter(element),
    set: (element: HTMLElement, value: any) => some_setter(element, value),
  },
}

The get function is called when populating the state object from the form. The passed element is the one that matches the selector, and it should return the value to be stored in the state object.

The set function is called when populating the form from the state object. The passed element is the one that matches the selector, and the value is that present within the state object. It should return nothing, but as a side effect it should update the form element to hold the given value.

[!NOTE]

Keep in mind that custom elements still need to alert formul8 when they change. If the altering event already bubbles up to the form, and formul8 is configured to recognize that event, no changes need to be made. Otherwise, the developer will need to dispatch an event that bubbles up to the form.

For example, say formul8 is not configured to recognize click events, but it does recognize change events. For a custom element that updates when it is clicked:

custom_input1.addEventListener("click", (ev) => {
  do_your_stuff(ev)
  // Send the parent form an alert that this input has changed.
  custom_input1.dispatchEvent(new Event("change", { bubbles: true }))
})

EXAMPLE

<form id="user_input_form">
  <input type="text" id="user_name" placeholder="Username..." />
  <input type="password" id="user_pass" placeholder="Password..." />

  <fieldset>
    <legend>Address</legend>

    <input type="text" id="line_1" placeholder="Line 1..." />
    <input type="text" id="line_2" placeholder="Line 2..." />
    <input type="text" id="city" placeholder="City..." />
    <input type="text" id="province" placeholder="Province..." />
    <input type="text" id="postal_code" placeholder="Postal code..." />
    <select id="country">
      <option>...</option>
      <option>Martinaise</option>
      <option>Revachol</option>
      <option>Seol</option>
      <option>Oranje</option>
    </select>
  </fieldset>

  <input type="checkbox" id="terms_of_service" />
  <label for="terms_of_service">I agree to the TOS</label>
  <input type="checkbox" id="newsletter" />
  <label for="newsletter">Sign me up for the newsletter</label>
</form>
const [user_input, change_event] = formul8("user_input_form")

// user_input: {
//     "address": {
//         "line_1": string,
//         "line_2": string,
//         "city": string,
//         "province": string,
//         "postal_code": string,
//         "country": string
//     },
//     "user_name": string,
//     "user_pass": string,
//     "terms_of_service": boolean,
//     "newsletter": boolean
// }

do_stuff(user_input)
document.addEventListener(change_event, () => do_stuff(user_input))

SEE ALSO