JSFiddle - React, Tailwind, and code Playground
HTML
<form action="register" method="post" id="myform"><input type="hidden" name="redirect" value=""><table><thead><tr><td colspan="2"><h2>Register</h2></td></tr></thead><tbody><tr><td><label for="username">Username:</label></td><td><input id="username" type="text" name="username" data-valid="false" data-validate="^\w[\w_]{2,}"><div data-info="Invalid username." class="indicator"></div></td></tr><tr><td><label for="email">E-Mail:</label></td><td><input id="email" type="text" name="email" data-valid="false" data-validate="^\w[\w_+\.-]{2,}@\w(\-?\w)*\.[a-zA-Z]{2,}"><div data-info="Invalid e-mail address." class="indicator"></div></td></tr><tr><td><label for="password">Password:</label></td><td><input id="password" type="password" name="password" data-valid="false" data-validate="\S{6,}"><div data-info="Passsword must be at least 6 letters long." class="indicator"></div></td></tr><tr><td><label for="password_confirm">Confirm Password:</label></td><td><input id="confirm" type="password" data-valid="true" data-confirm="password"><div data-info="Both password need to match." class="indicator"></div></td></tr></tbody><tfoot><tr><td colspan="2"><input type="submit" value="Register"></td></tr></tfoot></table></form>
CSS
div.indicator{width:16px;height:16px;margin:0 2px;display:inline-block;vertical-align:middle;position:relative;}
div.indicator.ok,input[data-valid="true"]+div.indicator{background:green}
div.indicator.fail,input[data-valid="false"]+div.indicator{background:red}
div.indicator.fail:hover:after,input[data-valid="false"]:focus+div.indicator:after{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;display:block;position:absolute;z-index:1;background-color:#fff;color:#333;text-shadow:none;border:1px solid #000;content:attr(data-info);font:11px sans-serif;padding:0 2px;width:200px;right:-35px;top:1.8em}
JavaScript
(function (ns, $) {
function validateInput() {
var input = $(this);
input.attr('data-valid', (input.val().search(input.attr('data-validate')) >= 0).toString());
console.log('validating input on ', input);
}
function confirmInput() {
var input = $(this);
input.attr('data-valid', (input.val() === $('#'+input.attr('data-confirm')).val()).toString());
console.log('confirming input on ', input);
}
var Validator = ns.Validator = function (form) {
var self = this;
this.form = $(form).bind('submit', function (e) {
var invalidInputs = self.form.find('input[data-valid=false]');
if (invalidInputs.length > 0) {
console.log('prevent!');
invalidInputs.eq(0).focus();
e.preventDefault();
}
});
this.form.find('input[data-validate]').each(validateInput).keyup(validateInput);
this.form.find('input[data-confirm]').each(function () {
var that = this;
confirmInput.apply(this);
console.log(this);
$('#'+$(this).attr('data-confirm')).keyup(function () {
console.log($(this).val());
confirmInput.apply(that);
});
}).keyup(confirmInput);
};
return ns;
})(window, jQuery);
new Validator('#myform');