LogoLogo
  • FAC Curriculum
  • archive
    • Node & npm introduction
    • developer
      • Programming Paradigms
    • handbook
      • Asking for help
      • Coaching groups
      • Code review
      • Course rules
      • Debugging
      • Employment schedule and material delivered in FAC20
      • GitHub Workflow
      • Glossary or terms
      • Presentation Guidance
      • Equality, Diversity and Inclusion
      • Installation guide
      • Learning circles
      • Mentoring guidance
      • What to expect from your mentors
      • One-day projects
      • Pair programming
      • Portfolio
      • Questions for problem solving
      • Progress Logs
      • Final project report
      • Managing software projects
      • Project Presentations
      • Project roles
      • Projects
      • Retrospectives
      • Role Circles
      • Safeguarding policy
      • Technical Spikes
      • System requirements
      • Tech for Better
      • User Manuals
      • Wellbeing Support
      • project-docs
        • What makes a mentor?
        • Product Handover
        • Sprint Planning
        • Tech for Better Presentations
        • User Research & Usability Testing
    • foundation
      • full-stack
        • Learning Outcomes
        • project
      • testing
        • project
        • spikes
  • docs
    • Contributing to the curriculum
    • Curriculum intent
    • Curriculum process
  • src
    • About our curriculum
    • course
      • Code of Conduct
      • Docker
      • .NET and Umbraco
      • Getting started
      • Founders and Coders coursebook
      • KSB's
      • Mini projects
      • Revision checklist
      • Svelte
      • TypeScript
      • handbook
        • Software Developer Handbook
        • Software Foundation Handbook
      • precourse
        • Before you start the course
        • Installation List
      • syllabus
        • developer
          • app
            • learning-outcomes
            • project
            • schedule
            • spikes
          • introduction
            • learning-outcomes
            • project
            • resources
            • schedule
          • week00-pre-course
            • We'd like you to spend some time before starting the course working on useful fundamentals.
            • spikes
          • week01-project01-basics
            • Employability introduction
            • Homework
            • learning-outcomes
            • Week of September 9th
            • project
            • resources
            • schedule
            • spikes
          • week02-project02-chatbot
            • employability
            • Homework
            • learning-outcomes
            • Week of September 16th
            • project
            • resources
            • schedule
            • spikes
          • week03-project03-server
            • Learning Outcomes
            • Week of September 23th
            • The Amazin' Quizzer API Backend
            • resources
            • schedule
          • week04-project03-frontend
            • learning-outcomes
            • Week of September 30th
            • UI for Quizzer App
            • resources
            • schedule
          • week05-project03-test-deploy
            • Testing and deployment
            • Week of October 7th
            • project
            • resources
            • schedule
          • week06-project04-databases
            • learning-outcomes
            • Week of October 14th
            • project
            • Databases
            • schedule
          • week07-project04-authentication
            • Learning Outcomes
            • Week of October 21st
            • project
            • resources
            • schedule
          • week08-project04-test-deploy
            • Learning Outcomes
            • Week of October 28th
            • project
            • resources
            • schedule
          • week09-reading-week
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
          • week10-project05-DOTNET-intro
            • Learning Outcomes
            • overview
            • project
            • Resources
            • schedule
          • week11-project05-DOTNET-testing
            • Testing and deployment
            • Week of November 18th
            • project
            • Resources
            • schedule
          • week12-project05-DOTNET-deploy
            • Learning Outcomes
            • Week of November 25th
            • project
            • Resources
            • schedule
            • Spikes
          • week13-TFB-design
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
            • Design Week Spikes
          • week14-TFB-build
            • Learning Outcomes
            • overview
            • Project
            • DevOps Resources
            • schedule
            • Spikes
          • week15-TFB-build
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
            • Spikes
          • projects
            • in-house-design
              • Learning Outcomes
              • Project
              • Resources
              • schedule
              • Design Week Spikes
        • foundation
          • Obsolete-full-stack
            • project
          • post-course
            • Homework
            • schedule
        • portfolio
          • fruit-shop
            • learning-outcomes
            • project
            • resources
          • game
            • learning-outcomes
            • project
            • resources
          • hobby-page
            • learning-outcomes
            • project
            • resources
          • movie-data
            • learning-outcomes
            • project
            • resources
          • project-gallery
            • learning-outcomes
            • project
            • resources
          • website
            • learning-outcomes
            • project
            • JavaScript
        • tfb
          • week 1
            • Introduction (45 minutes)
            • Further reading
          • week 10
            • content
            • resources
          • week 11
            • What will we be doing this week?
            • resources
          • week 12
            • What will we be doing this week?
            • Further reading
          • week 2
            • Discover (90 minutes)
            • resources
          • week 3
            • content
            • resources
          • week 4
            • Mapping the user journey (90 minutes)
            • resources
          • week 5
            • Figma Workshop 1 (90 minutes)
            • Further reading
          • week 6
            • Figma Workshop 2 (90 minutes)
            • resources
          • week 7
            • Product pitches & Selection (90 minutes)
            • resources
          • week 8
            • content
            • resources
          • week 9
            • content
            • resources
    • learn
      • DOTNET
        • Introduction to .NET
      • auth
        • Authenticating web apps
      • database
        • Persisting data with SQLite and Node
      • dotnet-two
        • Dependency injections and interfaces in .NET
      • form-validation
        • Form validation
      • react
        • Building client-side apps with React
      • server
        • HTTP servers with Node & Express
      • typescript
        • TypeScript
    • mentoring
      • design-week
        • Analysis Workshop
        • Code planning
        • Definition Workshop
        • Discovery Workshop
        • Figma introduction
        • Usability testing
        • User Research
    • resources
      • http
        • introduction
    • workshops
      • cookie-auth
        • index
      • creating-promises
        • index
      • css-layout
        • index
      • cypress-testing
        • index
      • database-testing
        • index
      • dev-tooling
        • Developer tooling
      • dom-challenge
        • index
      • dom-rendering
        • index
      • es-modules
        • index
      • express-middleware
        • Express middleware
      • first-class-functions
        • index
      • form-validation
        • index
      • functions-callbacks-async
        • Functions, callbacks, & async JavaScript
      • git-intro
        • Introduction to Git
      • git-terminal
        • Using Git in the terminal
      • git-workflow
        • Git workflow
      • github-projects
        • GitHub Projects Workflow Workshop
      • heroku-sql-challenge
        • index
      • html-forms
        • index
      • learn-a11y
        • index
        • starter-files
          • solution
            • Accessibility solution explanation
      • learn-fetch
        • index
      • learn-integration-testing
        • index
      • learn-testing
        • Learn testing in JavaScript
      • learn-unit-testing
        • index
      • node-error-handling
        • Node error-handling
      • node-express-server
        • Node and Express HTTP server
      • node-npm-intro
        • Node & npm introduction
      • node-postgres
        • Learn Postgres with Node
      • node-scripting-challenge
        • index
      • password-security
        • index
      • promise-practice
        • index
      • react-components
        • React components
      • react-fetch
        • index
      • react-forms
        • React forms
      • react-refactor-classes
        • index
      • react-state-effects
        • React state & effects
      • real-world-fetch
        • index
      • scope-challenge
        • Scope debugging challenge
      • semantic-html
        • index
      • server-side-forms
        • Server-side forms
      • session-auth
        • Session authentication
      • sql-intro
        • index
      • tdd-array-methods
        • index
Powered by GitBook
On this page
  • Setup
  • Why validate in the browser?
  • Communicating requirements
  • Required values
  • Different types of value
  • HTML5 validation
  • Requiring values
  • Types of values
  • Matching a pattern
  • Other validation
  • Challenge
  • Custom validation
  • Hijacking the built-in validation
  • Challenge
  • Handling invalid inputs
  • Challenge
  • Validation messages
  • Challenge
  • Re-validating
  • Styling
  • Challenge
  • Stretch goal: custom messages
  • Challenge
  • Resources
Export as PDF
  1. src
  2. workshops
  3. form-validation

index

Previousform-validationNextfunctions-callbacks-async

Last updated 4 years ago

In this workshop you'll learn how to validate user input in the browser and present error messages accessibly.

Final solution

Setup

  1. Download starter files

  2. Open workshop/index.html in your browser

  3. This is the form we'll be adding validation to

Why validate in the browser?

Client-side validation is important for a good user experience—you can quickly give the user feedback when they need to change a value they've entered. For example if passwords must be a certain length you can tell them immediately, rather than waiting for the form to submit to the server and receive an invalid response.

Communicating requirements

Our form has two inputs: one for an email address and one for a password. These are the requirements we need to validate:

  1. Both values are present

  2. The email value is a valid email address

  3. The password contains at least one number, and is at least 8 characters long

Before we implement validation we need to make sure the user is aware of the requirements, by labelling the inputs. There's nothing more frustrating than trying to guess what you need to do to be able to submit a form.

Required values

Different types of value

If we don't list our password requirements users will have to guess what they are.

The simplest way to list requirements is in a <div> following the label. This is fine for visual users but won't be linked to the input, which means assistive tech will ignore it.

Challenge

  1. Add a visual required indicator to both inputs.

  2. Add instructions containing our password requirements

  3. Associate the instructions with the input using aria-describedby

If you inspect the password input in Chrome's devtools you should be able to see the accessible name (from the label) and description (from the div) in the "Accessibility tab".

HTML5 validation

Now we need to tell the user when they enter invalid values. Browsers support lots of different types of validation.

Requiring values

The required attribute will stop the user submitting the form if they haven't entered this value yet.

<input required />

Types of values

Browsers will validate certain input types to make sure the value looks correct. For example:

<!-- checks the value is an email string -->
<input type="email" required />
<!-- checks the value is a URL string -->
<input type="url" required />

Matching a pattern

We can specify a regex the value must match using the pattern attribute. For example this input will be invalid if it contains whitespace characters:

<input type="text" pattern="\S" />

Other validation

You can even style inputs based on their validity using CSS pseudo-classes like :invalid, :valid and :required.

Challenge

Ensure each input meets our validation requirements above. If you submit the form with invalid values the browser will automatically stop the submission and show a warning.

Custom validation

It's still useful to start with the HTML5 validation attributes, so that if our JS fails to load or breaks the user at least gets basic validation.

Hijacking the built-in validation

First we need to disable the native validation by setting the novalidate attribute on the form element. This prevents the built-in errors from appearing.

Then we can listen for the form's submit event and check whether any inputs are invalid using the form element's .checkValidity() method.

This method returns true if all inputs are valid, otherwise it returns false. If any of the inputs are invalid we want to call event.preventDefault() to stop the form from submitting. Don't worry about showing error messages for now.

Challenge

  1. Open workshop/index.js

  2. Disable the native form validation

  3. Listen for submit events and check whether all the inputs are valid

  4. Prevent the form from submitting if any inputs are invalid

Handling invalid inputs

We've managed to stop the form submitting invalid values, but we need to provide feedback to the user so they can fix their mistakes.

First we need to actually mark the input as "invalid". The aria-invalid attribute does this. Each input should have aria-invalid="false" set at first, since the user hasn't typed anything yet. Then we need to know when the input becomes invalid, so we can update to aria-invalid="true".

We can listen for an input's invalid event to run code when it fails validation. The browser will fire this event for all invalid inputs when you call the form element's checkValidity() method. E.g.

inputElement.addEventListener("invalid", (event) => {
  console.log(inputElement + " is now invalid");
});

The final step is showing a validation message depending on what type of validation error occurred. We can access the default browser message via the input.validationMessage property. E.g. for a required input this might be "Please fill out this field".

Challenge

  1. Loop through all the inputs

  2. Mark each as valid

  3. For each input listen for the invalid event

    • Mark the input as invalid when this event fires

Validation messages

We need to actually tell the user what their mistake was. The simplest way to do this is to grab the built-in validation message from the browser. This will be available as the element.validationMessage property. For example if the user typed "hello" into this input:

<input type="email" />
<script>
  input.addEventListener("invalid", () => {
    console.log(input.validationMessage);
  });
</script>

The JS would log something like "Please include an '@' in the email address". These message vary across browsers.

We need to put the message on the page so the user knows what they did wrong. The message should be associated with the correct input: we want it to be read out by a screen reader when the user focuses the input.

We can achieve this using aria-describedby just like with our password requirements. This can take multiple IDs for multiple descriptions (the order of the IDs determines the order they will be read out).

<label for="password">Password</label>
<div id="passwordRequirements">
  Please enter at least 8 characters containing at least 1 number
</div>
<input
  id="password"
  type="password"
  aria-describedby="passwordRequirements passwordError"
  required
/>
<div id="passwordError">{insert the right message in here}</div>

Whenever this input is focused a screen reader will read out the label first, then the type of input, then any ARIA descriptions.

Challenge

  1. Create divs to contain the error messages

  2. Set attributes on the inputs and divs so they are linked together

  3. Put the validation messages inside the divs so the user can read them

Re-validating

Right now it's a little confusing for the user as the input stays marked invalid even when they type something new. We should mark each input as valid and remove the error message when the user inputs something.

  1. Add an event listener for input events

  2. Mark the input valid and remove the error message

Styling

We have a functional, accessible solution now, but it could be improved with some styling. It's common to style validation messages with a "danger" colour like red, and sometimes to mark invalid inputs with a different coloured border. You could also use warning icons to make errors even more obvious.

Challenge

  1. Style the error messages

  2. Style invalid inputs

  3. Add any other styles you like to make it look good

Stretch goal: custom messages

The default browser messages could be better. They don't contain specific, actionable feedback. E.g. if a pattern doesn't match the user sees "Please match the requested format". It would be more useful to show "Please enter at least one number".

We need to know what type of error occurred to show the right custom message. The input element's .validity property contains this information.

This interface has properties for every kind of error. For example an empty required input will show:

{
  valueMissing: true,
  typeMismatch: false,
  patternMismatch: false,
  // ... etc
}

We can write an if/else statement to check whether each property we're interested in is true. If it is we can show a custom error on the page:

let message = "";
if (validity.valueMissing) {
  message = "Please enter an email address";
} else if (validity.typeMismatch) {
  // ...
}

Challenge

  1. Edit your invalid handler to check the validity interface

  2. Show custom error messages based on the input's ID and what validation failed.

Resources

Users generally expect required fields to be (*). We can add one inside the <label>. However this will cause screen readers to read out the label as "email star", which is not correct. We should wrap the asterisk in an element with aria-hidden="true" to ensure it is ignored by assistive technology.

We need to use the attribute on the input. This takes the IDs of other elements that provide additional info. It allows us to link the div to the input so screen readers read out the extra info as if it were part of the label.

Accessibility tab example

Here's a .

Built-in validation is very simple to implement, and it works without JavaScript. However it has a few downsides. We cannot style the error message bubbles that pop up. The messages are . Required inputs are marked invalid as soon as the page loads (since they are empty). We can definitely improve this user experience by enhancing our validation with JS.

initial solution

Here's a list of .

Final solution

(built-in HTML validation)

(the limitations of HTML/CSS-only validation)

(explains the JS/ARIA stuff we need for accessible validation)

(summarises how aria-describedby works)

marked with an asterisk
aria-describedby
full list of validation attributes
not properly exposed to most screen readers
all the validity properties
Constraint Validation | MDN
Native form validation—part 1
Required attribute requirements
Describing aria-describedby