block by shawnbot a14c8ba59153b13f3c17ba33bf23ac07

Lightweight HTML5 form validation + ARIA

Full Screen

index.html

<link rel="stylesheet" href="aria-validated.css">

<form is="aria-validated">
  <label for="first_name">First Name</label>
  <input id="first_name" name="first_name" required>
</form>

<script src="aria-validated.js"></script>

aria-validated.css

label,
input {
  display: block;
}

input[aria-invalid=true][aria-label]::before {
  color: red;
  content: attr(aria-label);
  display: block;
}

aria-validated.js

(function(exports) {
  
  var onChange = function(e) {
    var input = e.target;
    var valid = input.validity.valid;
    input.setAttribute('aria-invalid', !valid);
    if (valid) {
      // XXX: restore previous value of aria-label, e.g. via data-aria-label
      input.removeAttribute('aria-label');
    } else {
      input.setAttribute('aria-label', input.validationMessage);
    }
  };
  
  var onKeyUp = function(e) {
    // only respond to keypress events for elements that have a name
    if (e.target.name) {
      return onChange.call(this, e);
    }
  };
  
  document.registerElement('aria-validated', {
    extends: 'form',
    prototype: Object.create(
      HTMLFormElement.prototype,
      {
        createdCallback: {value: function() {
          // HTML5 form validation polyfill
          H5F.setup(this);
        }},
        
        attachedCallback: {value: function() {
          // set aria-required="true" for all required inputs
          [].forEach.call(this.querySelectorAll('[required]'), function(input) {
            input.setAttribute('aria-required', true);
          });
          this.addEventListener('change', onChange);
          this.addEventListener('keyup', onKeyUp);
        }},
        
        detachedCallback: {value: function() {
          this.removeEventListener('change', onChange);
          this.removeEventListener('keyup', onKeyUp);
        }}
      }
    )
  });
  
})(this);