JSFiddle - React, Tailwind, and code Playground

by mcsf

HTML

<p><strong>
  Focus the first field, then keep trying to submit by pressing <kbd>ENTER</kbd>.<br>Watch as the browser guides you through the form to fix all the inputs.
</strong></p>

<form
	onsubmit="doSubmit(event, this)"
	onchange="validate(this)"
>
	Name (not required):
	<input>
	<br>

	Name (required):
	<input required>
	<br>

	Name (at least three characters):
	<input required minlength="3">
	<br>

	Full name (needs at least two separate words):
	<input required pattern="\w+( \w+)+">
	<br>

	Full name (uses custom message to inform user):
	<input required pattern="\w+( \w+)+" onchange="validateFullName(event)">
	<br>

	Email (required, needs valid email address):
	<input required type="email">
	<br>

	Email (with custom rules):
	<input required type="email" x-validate="NO_SPAIN_TLD ARTURITOS_ONLY">
	<br>

	<input type="submit">
</form>

CSS

body {
  background-color: #eee;
}

JavaScript

// Ad-hoc validation for the `fullName` input
function validateFullName({target}) {
  target.setCustomValidity(
    target.validity.patternMismatch
    ? 'Your full name should include at least a first name and a surname.'
    : ''
  );
}


// Micro-framework using the `x-validate` custom attribute
const VALIDATIONS = {
  NO_SPAIN_TLD: [input => !input.value.match(/\.es$/), 'No Spain, plz!'],
  ARTURITOS_ONLY: [input => input.value.match(/^arturito/), 'Only Arturitos!'],
};

function doSubmit(event, form) {
  event.preventDefault();
  alert('Submitted');
}

function validate(form) {
  form.querySelectorAll('[x-validate]').forEach(input => {
    const validations = input.getAttribute('x-validate').split(' ');
    for (const v of validations) {
      if (VALIDATIONS[v]) {
        const [predicate, message] = VALIDATIONS[v];
        if (! predicate(input)) {
          input.setCustomValidity(message);
          break;
        }
        input.setCustomValidity('');
      }
    }
  });
}