JSFiddle - React, Tailwind, and code Playground
HTML
<label>
<input type="email" name="test1" />
</label>
<label>
<input type="password" pattern=".{8}.+*" name="test2" required="required" />
</label>
<label>
<select id="example" name="test" required="required">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</label>
<label>
<textarea name="test" required="required"></textarea>
</label>
CSS
label {
display: block;
margin: 1rem;
}
label:after {
content: attr(data-invalid);
color: red;
font-size: x-small;
}
JavaScript
(function() {
var ATTRIBUTE_NAME = "data-invalid";
var labels = document.getElementsByTagName("label");
function getLabelOf(element) {
if (element.id) {
var index = labels.length;
while (--index >= 0) {
var label = labels[index];
if (label.htmlFor === element.id) {
return label;
}
}
}
var ancestor = element.parentNode;
while (ancestor) {
if (ancestor.tagName === "LABEL") {
return ancestor;
} else {
ancestor = ancestor.parentNode;
}
}
return null;
}
function updateValidityAttributeOf(element) {
var label = getLabelOf(element);
if (label) {
if (element.validity.valid) {
label.removeAttribute(ATTRIBUTE_NAME);
} else {
label.setAttribute(ATTRIBUTE_NAME, element.validationMessage);
}
}
}
[HTMLSelectElement, HTMLInputElement, HTMLTextAreaElement].forEach(function(element) {
var prototype = element.prototype;
var setCustomValidity = prototype.setCustomValidity;
prototype.setCustomValidity = function(message) {
setCustomValidity.call(this, message);
updateValidityAttributeOf(this);
};
});
document.addEventListener("input", function(event) {
updateValidityAttributeOf(event.target);
}, false);
var inputs = document.getElementsByTagName("input");
var selects = document.getElementsByTagName("select");
var textareas = document.getElementsByTagName("textarea");
var forEach = [].forEach;
document.addEventListener("DOMContentLoaded", forEach.bind([inputs, selects, textareas], function(nodelist) {
forEach.call(nodelist, updateValidityAttributeOf);
}), false);
}());
document.addEventListener("DOMContentLoaded", function example() {
document.getElementById("example").setCustomValidity("CUSTOM!");
}, false);