Enhancement

FormEnhancer

Accessible error messages for the browser's own form validation

Import
@parallelogram-js/core/components/FormEnhancer
Selector
form[data-form-enhancer]

Playground

Booking form

Leave a field empty or submit to see the messages.

<form data-form-enhancer class="form" action="#booked">
  <div class="form__group">
    <label class="form__label" for="guest-name">Name</label>
    <div class="form__control">
      <input id="guest-name" name="name" required minlength="2" autocomplete="name">
    </div>
  </div>
  <div class="form__group">
    <label class="form__label" for="guest-email">Email</label>
    <div class="form__control">
      <input id="guest-email" name="email" type="email" required autocomplete="email"
             data-form-enhancer-message-value-missing="Tell us where to send your confirmation">
      <p data-error-for="email" hidden></p>
    </div>
  </div>
  <div class="form__group">
    <label class="form__label" for="guest-party">Party size</label>
    <div class="form__control">
      <input id="guest-party" name="party" type="number" min="1" max="12" required>
    </div>
  </div>
  <div class="form__actions">
    <button type="submit">Book</button>
  </div>
</form>

About

Fields declare their rules with native attributes (required, type, minlength, maxlength, min, max, step and pattern), so the form still validates when scripts don't run. Once mounted, FormEnhancer sets novalidate on the form and shows the messages itself: after the user leaves a field they changed, and for every field when they submit. A blocked submission moves focus to the first invalid field.

Each error is written to the form's [data-error-for] element for that field, or to one created after it. The field gets aria-invalid="true" and an aria-describedby link to the message. Messages come from the browser's localised validationMessage unless the field supplies its own. Custom rules use setCustomValidity() on the field.

Usage

Register the component and the framework loads it the first time a page contains form[data-form-enhancer].

import { Parallelogram } from '@parallelogram-js/core';

const app = Parallelogram.create();
app.components.add('form[data-form-enhancer]', () => import('@parallelogram-js/core/components/FormEnhancer'));
app.run();

The framework is optional. FormEnhancer.enhanceAll() mounts the component on every matching element by itself, for a page that wants one enhancement rather than a framework:

import FormEnhancer from '@parallelogram-js/core/components/FormEnhancer';

FormEnhancer.enhanceAll();

It takes a selector and the component's options, and returns the instance it mounted them with. What it leaves out is what the framework adds: loading a component only on the pages that use it, mounting again after a page swap, and ordering with dependsOn.

Attributes

NameTypeDefaultDescription
data-form-enhancerflagnoneMarks the form On the form.
data-form-enhancer-validate-on-inputbooleantrueRe-check a field that shows an error as it is edited For every instance, FormEnhancer.defaults.validateOnInput.
data-form-enhancer-validate-on-blurbooleantrueCheck a changed field when the user leaves it For every instance, FormEnhancer.defaults.validateOnBlur.
data-form-enhancer-show-errors-immediatelybooleanfalseCheck fields while the user types For every instance, FormEnhancer.defaults.showErrorsImmediately.
data-form-enhancer-validate-debouncenumber300Typing pause in milliseconds before checking, when errors show immediately For every instance, FormEnhancer.defaults.debounce.
data-form-enhancer-error-classstringis-invalidClass added to invalid fields For every instance, FormEnhancer.defaults.errorClass.
data-form-enhancer-valid-classstringis-validClass added to checked fields that are valid For every instance, FormEnhancer.defaults.validClass.
data-form-enhancer-message-classstringform__errorClass on a message element the form didn't supply itself For every instance, FormEnhancer.defaults.messageClass.
data-form-enhancer-messagestringnoneThe message for any failure On a field.
data-form-enhancer-message-<constraint>stringnoneThe message for one constraint: value-missing, type-mismatch, bad-input, pattern-mismatch, too-short, too-long, range-underflow, range-overflow or step-mismatch On a field.
data-error-forstringnoneThe field's name On the element that shows a field's error.

Events

NameDetailDescription
form-enhancer:mounted{ element: HTMLFormElement; fieldCount: number }The form was set up DOM event and event bus
form-enhancer:submit-blocked{ element: HTMLFormElement; errors: Array<[string, string]> }A submission was stopped; errors pairs each field name with its message DOM event and event bus
form-enhancer:submit-valid{ element: HTMLFormElement }Every field passed and the submission goes ahead DOM event and event bus