Hanzo
Hanzo Skills Reference

Hanzo Form.js

Form.js is a lightweight browser library for automatic form validation and submission. It intercepts native HTML form submit events, emits lifecycle events (init, submit, done), and provides an XHR...

Overview

Form.js is a lightweight browser library for automatic form validation and submission. It intercepts native HTML form submit events, emits lifecycle events (init, submit, done), and provides an XHR helper for posting form data as JSON. Zero dependencies beyond little-emitter. Written in CoffeeScript, ships as both CommonJS and ES module bundles. Designed as a companion to Hanzo's mailing list and lead capture forms.

Tech Stack

  • Language: CoffeeScript (compiled to JS)
  • Runtime dependency: little-emitter (tiny event emitter, ~150 bytes)
  • Build: Cakefile (via shortcake + cake-bundle using requisite)
  • Output: CommonJS (lib/form.cjs.js) + ES module (lib/form.es.js)
  • Tests: Mocha + Chai + Selenium WebDriver + PhantomJS
  • CI: Travis CI
  • npm package: form.js v0.2.0

OSS Base

Repo: github.com/hanzoai/form.js (branch: master, 16 stars).

When to use

  • Intercepting HTML form submissions to send data via XHR/AJAX instead of page reload
  • Adding custom validation logic before form submit
  • Capturing mailing list signups or lead generation forms
  • Lightweight form handling without a full framework (no jQuery required)

Hard requirements

  1. Browser environment with DOM (not Node.js server-side)
  2. HTML <form> element in the page -- the library auto-discovers the last form on the page at load time
  3. Script tag must be inside or after the form (uses getElementsByTagName to find the last <script> and <form>)

Quick reference

ItemValue
npm packageform.js
Version0.2.0
Default branchmaster
LicenseBSD-3-Clause
LanguageCoffeeScript
CJS entrylib/form.cjs.js
ES entrylib/form.es.js
Globalwindow.Inform
Runtime deplittle-emitter only
Repogithub.com/hanzoai/form.js

One-file quickstart

Script tag integration

<form action="/subscribe" method="POST">
  <input type="email" name="email" placeholder="Email" required />
  <input type="text" name="firstName" placeholder="First Name" />
  <button type="submit">Subscribe</button>

  <script src="https://unpkg.com/form.js@0.2.0/lib/form.cjs.js"></script>
  <script>
    var Inform = window.Inform

    // Listen for form submission (prevents default, gives you control)
    Inform.on('inform-submit', function(done, event) {
      var formData = new FormData(event.target)
      var data = {}
      formData.forEach(function(value, key) { data[key] = value })

      fetch('/api/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      })
      .then(function() { done() })  // Call done() to fire the 'done' event
      .catch(function(err) { console.error(err) })
    })

    // Listen for successful submission
    Inform.on('inform-done', function(event) {
      alert('Thanks for subscribing!')
    })
  </script>
</form>

npm install

npm install form.js
// ES module
import Inform from 'form.js'

Inform.on('inform-submit', function(done, event) {
  // Handle submission
  done()
})

Core Concepts

Architecture

┌────────────────────────────────────────┐
│  HTML Page                              │
│                                         │
│  <form>                                 │
│    <input name="email" />               │
│    <button type="submit">Go</button>    │
│                                         │
│    <script src="form.js"></script>       │
│  </form>                                │
│                                         │
│  ┌──────────────────────────────────┐   │
│  │  Inform (EventEmitter)           │   │
│  │                                  │   │
│  │  Events:                         │   │
│  │  1. init-inform-script  ─────>  script tag ref    │
│  │  2. init-inform-form    ─────>  form element ref  │
│  │  3. init-inform-inputs  ─────>  input elements    │
│  │  4. init-inform-submits ─────>  submit buttons    │
│  │  5. inform-submit       ─────>  (done, event)     │
│  │  6. inform-done         ─────>  completion         │
│  └──────────────────────────────────┘   │
└────────────────────────────────────────┘

Event Lifecycle

  1. DOM ready -- Library auto-initializes, finds the last <form> and <script> tags
  2. init-inform-script -- Fires with the script element reference
  3. init-inform-form -- Fires with the form element reference
  4. init-inform-inputs -- Fires with array of input/select elements (excludes submit buttons)
  5. init-inform-submits -- Fires with array of submit buttons (input[type=submit] + button[type=submit])
  6. inform-submit -- Fires on form submit with (done, event). Call done() to proceed to inform-done. The default form submit is prevented.
  7. inform-done -- Fires after done() is called, re-dispatches the native submit event

Global API

The library exposes window.Inform as an event emitter with standard methods:

  • Inform.on(event, callback) -- Listen for an event
  • Inform.off(event, callback) -- Remove a listener
  • Inform.emit(event, ...args) -- Emit an event
  • Inform.Events -- Map of event name constants
  • Inform.events -- Same map (alias)

XHR Helper (src/xhr.coffee)

A minimal XMLHttpRequest wrapper with a post(url, headers, payload, callback) method. Used internally for legacy AJAX form submission. Supports IE via ActiveXObject fallback.

Source Files

FilePurpose
src/index.coffeeMain library: auto-init, event lifecycle, form interception
src/xhr.coffeeXHR POST helper class

Directory structure

form.js/
  package.json          # npm config, v0.2.0
  Cakefile              # Build/test tasks (shortcake + cake-bundle)
  README.md             # Minimal README
  LICENSE               # BSD-3-Clause
  .travis.yml           # Travis CI config
  src/
    index.coffee        # Main library (event emitter + form interception)
    xhr.coffee          # XMLHttpRequest POST helper
  test/
    test.coffee         # Selenium WebDriver integration tests
    test.html           # Test harness HTML page
    inform.js           # Built test bundle
    util.coffee         # Test utilities
    ci-config.coffee    # Cross-browser Sauce Labs config
    jquery-1.11.3.min.js  # jQuery for test assertions
  lib/                  # Built output (generated)
    form.cjs.js         # CommonJS bundle
    form.es.js          # ES module bundle

Build and Test

# Install dependencies
npm install

# Build (generates lib/form.cjs.js and lib/form.es.js)
cake build

# Run tests (starts static server + Selenium + Mocha)
cake test

# Clean build artifacts
cake clean

Troubleshooting

IssueCauseSolution
window.Inform is undefinedScript loaded before form.js bundleEnsure form.js script tag is loaded
Form submits normally (page reload)Script tag not inside/after the formPlace <script> inside the <form> or after it
inform-submit not firingForm has no submit buttonAdd <button type="submit"> or <input type="submit">
Events fire twiceDOMContentLoaded and load both trigger initLibrary guards against double-init internally
done() not workingNot calling the first argument of inform-submit callbackSignature is function(done, event) -- call done() to proceed
  • hanzo/hanzo-checkout.md - Full checkout widget (uses similar patterns)
  • hanzo/hanzo-commerce.md - Hanzo Commerce frontend

How is this guide?

Last updated on

On this page