Native form validation 2: HTML and JavaScript

In this second part of a three-part article we will continue our study of native form validation in browsers. Part 1 discussed general UI considerations and CSS. Part 3 will discuss the native error messages and offer general recommendations to come to actually usable native form validation.
In this part we’re going to take a look at a few HTML features and the JavaScript API.
((This article was originally published on Samsung Internet’s Medium channel. Since I do not believe Medium will survive in the long run I re-publish it here.)
As usual in my articles, I’m quite vague about exact browser compatibility patterns because I already collated that information in the inevitable compatibility table. You can find the gory details there.
HTML attributes
HTML supports many potentially useful input types and attributes. I did the basic research a while ago, and while some details will have changed, the overall picture is still that most browsers support most features decently.
Here I want to draw attention to two features missing from my old overview: how the title attribute affects error messages, and the novalidate attribute.
title
It’s simple, really. The content of the title attribute of a field is added to the field’s error message only if the field has a pattern. This is useful for giving clues about the exact nature of the pattern; something that is impossible for the browser to determine.
It would also be useful to use the title for giving clues about the exact nature of fields that do not have a pattern, but, as we’ll see throughout this article, we can’t have nice things because that would make things nice for us. And we’re born to suffer. So title only works on pattern.
novalidate
The novalidate attribute of forms works in most browsers. When present, the attribute tells the browser not to attempt any native validation. In addition to suppressing the native error messages it also suppresses all the rest of validation, so the form is submitted unless an old-fashioned form validation script that you wrote yourself prevents it.
If you want to retain part of native validation, but not the error messages, you have to use the invalid event, which will be explained in part 3.
The Constraint Validation API
Let’s turn to the JavaScript side of things. We will find an entirely different set of problems than in CSS that preclude useful form validation for entirely different reasons.
The Constraint Validation API is part of the HTML5 specification and that doesn’t really do a lot of useful things. (Gem: a form field value can be “suffering from being missing.”) Browsers support this API fairly well, with only one method lacking in older browsers. Unfortunately this is exactly the best-designed and most useful method.
Also, the creators of this spec did not pay any attention to what the CSS people were doing with :invalid. Here’s an example:
As we saw in part 1, fieldset:invalid works in most browsers and kicks in when at least one form field in the fieldset is invalid. The API allows us to use the checkValidity() method on fieldsets as well, but it returns true, even when the fieldset contains an invalid form field. (To make matters more complicated, several Chromia, but not the latest Google Chrome itself, implement checkValidity() on fieldsets correctly.)
Right hand, meet left hand. The two of you should connect one of these days.
validity
But anyway. Let’s start with an API feature that actually works. Every form field has a validity property that contains a bunch of information about its invalidity. All browsers support nearly all properties, even though only a few are actually useful.
All properties come in the form formField.validity.propertyName. They are best summarised in table form:

Property
Applies to
is true when

badInput
number
the value is not a number

patternMismatch
pattern
the value does not conform to the pattern

rangeOverflow
number
the value is higher than the max attribute

rangeUnderflow
number
the value is lower than the min attribute

stepMismatch
number
the value does not conform to the step attribute

tooLong
maxlength
the user has attempted to add a character to a form field with a too-long default value

tooShort
minlength
the user has entered a character in the field, but there are fewer characters than the minlength value

typeMismatch
email or URL
the value is not an email address or a URL

valid
any
the field is valid

valueMissing
required
the field is empty

The properties that deal with number fields are useful: we can figure out exactly what kind of error the user made, and adjust our error messages accordingly.
Unfortunately the other properties are rather pointless. If there’s an error in an email, url, required, or pattern field it’s immediately clear what the problem is. The extra properties of validity are not necessary.
It would be useful if we’d get more specific data, such as “user hasn’t entered an @ in this email field.” Native error messages in fact do so in some browsers, but the validity properties don’t.
At least these properties do not actively harm native form validation UX. You will start to appreciate such slight blessings before we’re done with the API.
The tooLong saga
And then there’s the tooLong saga. This part of my research took way too long because the browsers saw fit to implement maxlength and minlength in a way that’s entirely different from all other constraints. I see no reason not to share my pain with you.
Take the following form field, and note it has a default value. If we validate it straight away we get the validity.typeMismatch error we would expect:

Link: http://www.quirksmode.org/blog/archives/2017/12/native_form_val_1.html