HTML, CSS & the Browser

Form Validation

Definition

HTML gives you validation with no JavaScript at all. required makes a field mandatory, type="email" and type="url" check the shape of a value, min, max and step bound a number, and pattern takes a regular expression the value has to match. When a control fails, the browser blocks submission and shows its own bubble, and the field matches the :invalid pseudo-class so you can style it. JavaScript goes further through the Constraint Validation API: field.checkValidity() returns true or false, and field.setCustomValidity('Enter a UK phone number') replaces the browser's wording with your own. What none of this does is make the data safe. Client-side validation is a convenience for the person filling the form, never a security control, because anyone can delete the attribute in the Elements panel or post to your endpoint directly.

Why It Matters

A form that rejects good input costs you the enquiry. A pattern written for UK postcodes rejects Irish ones; type="email" accepts a@b, which is technically valid and almost never what the person meant. In the other direction, mistaking the browser's check for protection is how a page ends up forwarding whatever arrives straight into an inbox or a spreadsheet. Whatever receives the submission has to validate again on its own side — that is the only check an attacker cannot switch off in two clicks. Between those extremes, the built-in rules catch nearly everything a real visitor gets wrong by accident.

How It Works

Submission runs the form's constraint check first. The browser walks the controls in document order, tests each against its attributes and stops at the first failure, focusing that field and showing a message beside it — which is why a failing field inside a collapsed section looks like a submit button that does nothing. Adding novalidate to the form turns the built-in pass off entirely so your own code can run instead. In JavaScript you listen for the invalid event on a field, call event.preventDefault to suppress the native bubble and render your own message; setCustomValidity with a non-empty string marks the field invalid and setting it back to an empty string clears it. The :invalid selector applies from the moment the page loads, so styling it red usually wants :user-invalid or a class your script adds after the first attempt.

Real-World Example

A registration page for a short course is published at spring-intake.99helpers.site and a quarter of visitors give up at the form. The console has nothing in it; the Elements panel shows the phone field carrying pattern="[0-9]{11}", which rejects any number typed with spaces in it. Loosening the pattern and stripping spaces in the handler recovers those drop-offs. The endpoint behind the form still checks the number itself, because the rule in the browser is only there to catch honest mistakes.

Common Mistakes

  • Relying on required and the email type as protection — both are attributes in a page the visitor controls, and removing them takes two clicks
  • Writing a pattern stricter than reality — a postcode or phone expression that rejects valid foreign formats loses real enquiries in silence
  • Hiding a failing required field in a collapsed section — the browser tries to focus a control nobody can see, and the form appears frozen

Related Terms

Put a file online in seconds

Drop in a document, an image, a page or a whole static website and share the link — free, with no build step and no server to set up.

Host a file free →