Form novalidate vs. custom validity on number inputs with non‑multiple steps
0 reputation · 17 Jan 2021, 06:39 UTC
0 reputation · 17 Jan 2021, 06:39 UTC
When a form carries the novalidate attribute, browsers suppress native constraint checks but still allow JavaScript to apply custom validity messages via setCustomValidity. Recent tests show Safari silently clears these messages on submission, Chrome and Edge flag a typeMismatch for values that are not exact multiples of the declared step, and Firefox accepts the value unless it lies outside min/max. The timing of setCustomValidity after the submit event also differs: Chrome applies the message immediately, whereas Safari defers until the next validation cycle. These inconsistencies raise questions about the intended cross‑browser contract for custom validation on input type="number" when novalidate is present.
novalidate upon submission?typeMismatch on non‑multiple step values when novalidate is used?setCustomValidity invoked after the submit event be applied synchronously across browsers?34775 reputation · 17 Jan 2021, 07:41 UTC
When a form has novalidate, browsers are not required to run constraint validation on submission. That means custom validity messages set with setCustomValidity are not automatically shown or enforced by native submission logic. Safari clearing those messages on submit is consistent with the spec: novalidate bypasses the validation algorithm, and any UI that appears is implementation-specific. Chrome and Edge reporting typeMismatch for a value that is not an exact multiple of step is almost certainly a misreading of the ValidityState object; the standard property for that condition is stepMismatch, not typeMismatch. Firefox’s behavior—accepting the value unless it violates min/max—aligns with the fact that novalidate suppresses step checking. Finally, calling setCustomValidity after the submit event should not be expected to block submission synchronously across browsers; the spec does not define a cross-browser contract for late custom errors when novalidate is present.
Confirmed by the HTML standard: novalidate skips constraint validation during form submission, and input type="number" produces a stepMismatch state for values that are not multiples of step. Also confirmed: setCustomValidity synchronously updates the customError property on the element’s ValidityState. The likely explanation for the browser differences you observed is that native UI feedback for custom errors is an implementation detail of the constraint validation algorithm, which novalidate bypasses entirely. Because the algorithm is skipped, browsers are free to handle any residual message state differently, and timing after submit is not standardized.
Do not rely on native submission blocking or automatic message display when novalidate is present. Attach a submit listener, call event.preventDefault(), and manually invoke checkValidity() or reportValidity() on the form or specific inputs. This gives you deterministic behavior across browsers.
form.addEventListener('submit', (e) => {
e.preventDefault();
const input = document.getElementById('qty');
input.setCustomValidity(''); // clear first
if (input.validity.stepMismatch) {
input.setCustomValidity('Quantity must be a multiple of 2.');
}
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// proceed with submission
});
To confirm the exact validity state in your environment, build a minimal case and log the properties:
novalidate, an input type="number" step="2" value="3", and a submit listener.input.validity.stepMismatch, input.validity.customError, and event.defaultPrevented.setCustomValidity('test') and then checkValidity() to see whether the message appears and whether submission is blocked.novalidate and repeat to observe how native stepMismatch and custom validity interact without the attribute.If your logs show typeMismatch rather than stepMismatch for a non-multiple step value, treat that as a reporting discrepancy and verify the exact ValidityState dump and browser version. Otherwise, the manual preventDefault + checkValidity pattern is the robust cross-browser approach.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.