I have an example fiddle of what I'm trying to accomplish: https://jsfiddle.net/qscL4gjh/1/
The input box must be a "text" input (not "number") and only allow either a blank value or 8 numeric digits. I have that part working.
But I want to show an error block (underneath the input) if the user enters a key value that does not meet the requirements - and for some reason I cannot get javascript to recognize the error block. I want the error block to appear for 3 seconds and fade out.
function validateObj(obj, evt) {
var key = (evt.which) ? evt.which : evt.keyCode
var b = (!(key > 31 && (key < 48 || key > 57)) && obj.value.length < 9);
if (!b) {
var a = obj.getAttribute("data-errorid");
console.log("Error Block Id: " + a);
var $err = document.querySelector(a) || obj.nextElementSibling;
if ($err.length) {
console.log("Found error note");
$err.style.visibility = "visible";
$err.style.opacity = "1";
$err.style.transition = "3s";
$err.style.opacity = "0";
}
else {
console.error("Could not find error block");
}
}
return b;
}
<label>Test 1</label>
<input type="text" id="num" value="" onkeypress="return validateObj(this, event)" data-errorid="#error_note1" value=""/>
<div id="error_note1" class="error_note">
Must be empty or 8 numeric digits
</div>
<br><br>
<label>Test2</label>
<input type="text" id="num" value="" onkeypress="return validateObj(this, event)" data-errorid="#error_note2" value=""/>
<div id="error_note2" class="error_note">
Must be empty or 8 numeric digits
</div>
In the example below:
Everything is wrapped in <form id='UI'>
and is referenced via .forms
property.
const UI = document.forms.UI;
Do not use inline attribute events, inline event handlers are garbage. Use onevent properties or .addEventListener()
. If you have multiple <input>
s, register events on an ancestor tag like <form>
, <body>
, document
, or window
. Read about events and event delegation
onkeypress="return validateObj(this, event)" //👎
.addEventListener(event, event handler, capture)
UI.addEventListener('keydown', resetVal); //👍
UI.addEventListener('keyup', valData, true); //👍
/* ☝ certain events require the capture phase instead of the bubbling phase
(see previous link to "event") */
OR onevent property
UI.onkeydown = resetVal; // 👍
UI.onkeyup = valData; // 🤔
/* "keyup" events actually fire on the capture phase that's why
.addEventListener() is recommended */
To always refer to the <input>
the user is currently typing into, use the Event.target
property.
const inp = e.target; // Refers >inp< as the <input> the user is typing into
if (inp.matches('input')) {... // Delegate event to >inp< only
.value
of the <input>
which is a String is being validated vs. the [pattern]
attribute of the <input>
which is RegExp.
<input id='test1' pattern='\d{8}'>
const data = inp.value;
const error = inp.nextElementSibling;
const pattern = inp.pattern;
const rgx = new RegExp(pattern, 'gm');
const match = rgx.test(data);
All styles are by class via .classList
property.
if (!match) {
inp.classList.add('red');
error.classList.add('flash');
} else {
inp.classList.remove('red');
error.classList.remove('flash');
}
This example will accommodate virtually an unlimited number of <input>
(static and dynamic) and with minor modifications can also accept from <textarea>
as well or contenteditable
tags ([data-pattern]
attribute and .textContent
and .dataset
properties).
Note: Flashing error messages isn't really good UX so I added the red text as a persistent reminder to the user that the text is still invalid.
const UI = document.forms.UI;
UI.addEventListener('keydown', resetVal);
UI.addEventListener('keyup', valData, true);
function resetVal(e) {
const inp = e.target;
if (inp.matches('input')) {
inp.nextElementSibling.classList.remove('flash');
if (inp.value.length < 1) {
inp.classList.remove('red');
}
}
};
function valData(e) {
const inp = e.target;
if (inp.matches('input') && inp.value.length > 0) {
const data = inp.value;
const error = inp.nextElementSibling;
const pattern = inp.pattern;
const rgx = new RegExp(pattern, 'gm');
const match = rgx.test(data);
if (!match) {
inp.classList.add('red');
error.classList.add('flash');
} else {
inp.classList.remove('red');
error.classList.remove('flash');
}
}
};
label {
display: block;
}
.error {
display: block;
width: 30ch;
margin: 3px 0 6px 0;
visibility: hidden;
text-align: center;
}
.flash {
visibility: visible;
opacity: 1;
animation: fade 3s forwards;
}
@keyframes fade {
0%,
100% {
opacity: 0
}
50% {
opacity: 1
}
}
.red {
color: red;
}
<form id='UI'>
<label>Test 1 <input id="test1" pattern='\d{8}'>
<output class="error">
Must have 8 numeric digits
</output></label>
<label>Test 2 <input id="test2" pattern='^[a-zA-Z]+$'>
<output class="error">
Must only have letters
</output></label>
<label>Test 3 <input id="test3" pattern='^[^\-\^\\\]()@#$%&*_+=~;:]+$'>
<output class="error">
Cannot have special characters
</output></label>
</form>