Tags: web-dev concept

Forms

Date: 2026-08-16


The platform’s most feature-rich element, and the one most often replaced with a worse version. Every custom form reimplements validation, keyboard behaviour, autofill and submission — and autofill alone moves checkout completion more than most redesigns.


What it is

A form is a group of controls the browser knows how to validate, serialise and submit. Wrapping fields in <form> isn’t decoration — a set of inputs outside a form is a set of inputs with none of the behaviour below.

What the element gives you

BehaviourRequires
Enter key submitsA <form> with a submit button
Built-in validationrequired, type, pattern, min, max
Autofill<form>, correct type, correct autocomplete
Password manager save promptA real <form> submission
Correct mobile keyboardtype="email", tel, number, url
Data serialised for younew FormData(form)
Works without JavaScriptaction and method

Enter-to-submit is the one people notice. A “form” built from divs and a click handler doesn’t submit on Enter, and users type Enter constantly.

Autofill is the commercial one

Browsers fill addresses, names, emails and card details automatically — but only when the fields declare what they hold.

<!-- autofills reliably -->
<input type="email" name="email" autocomplete="email">
<input type="text"  name="postcode" autocomplete="postal-code">
<input type="text"  name="address1" autocomplete="address-line1">
<input type="tel"   name="phone" autocomplete="tel">
<input type="text"  name="cc-number" autocomplete="cc-number" inputmode="numeric">
 
<!-- autofills unpredictably or not at all -->
<input type="text" name="field_2">
<input type="text" name="emailAddress" autocomplete="off">

autocomplete="off" on checkout fields is a self-inflicted conversion loss and browsers increasingly ignore it anyway. The full token list is worth checking against the HTML specification rather than guessed — [CHECK: current autocomplete token list].

Related: inputmode="numeric" for postcodes and card numbers gives a numeric keypad without type="number", which brings spinners and rejects leading zeros.

Validation

Native constraint validation runs before submit, free:

<input type="email" required>
<input type="text" pattern="[A-Za-z]{2}[0-9]{1,2} ?[0-9][A-Za-z]{2}" required>

The tradeoff is that native error messages can’t be styled and their wording is the browser’s. The usual approach:

form.addEventListener('submit', e => {
  if (!form.checkValidity()) {
    e.preventDefault();
    showOwnMessages(form);   // your styling, native validity state
  }
});

That keeps the browser’s validity logic and replaces only the presentation. input.validity exposes exactly what failed (valueMissing, typeMismatch, patternMismatch), so you rarely need to reimplement the checking.

Validation timing matters more than validation itself. Validating on every keystroke tells someone their email is invalid while they’re still typing it. The workable pattern is validate on blur, then revalidate on input once a field has already errored — see Form Design.

Accessibility essentials

  • Every field needs a <label for> pointing at its id. Placeholder text is not a label — it disappears on focus and isn’t reliably announced
  • Associate errors with aria-describedby so screen readers read the message with the field
  • Mark invalid fields with aria-invalid="true"
  • Never rely on colour alone for error state — Colour Contrast, Accessible Forms
  • Move focus to the first error on failed submit, or a keyboard user has no idea what happened

Submission and measurement

For instrumentation, the submit event is the right hook — it fires for Enter and for button clicks alike, where a click handler on the button misses Enter entirely.

form.addEventListener('submit', () => {
  dataLayer.push({ event: 'checkout_step_completed', step: 'delivery' });
});

But fire the success event on the server’s response, not on submit — a submitted form that fails validation server-side or fails payment is not a completed step. That distinction is the trigger specification in Tracking Plans, and getting it wrong produces conversions for failed payments.

Field-level abandonment analysis is the highest-yield checkout diagnostic there is — Form Analytics.

Failure modes

  • <div> instead of <form> — no Enter, no autofill, no password manager
  • autocomplete="off" on address or payment fields
  • type="number" for postcodes and card numbers — spinners, dropped leading zeros, locale-dependent parsing
  • Validating on keystroke, so errors appear mid-typing
  • Custom selects replacing <select> — the most commonly broken control on mobile, where the native one is a well-designed OS component
  • Tracking on button click rather than form submit, silently missing every Enter-key submission