I'm using javascript to check required fields in a form, so that a field is styled and an error message appears if it's empty on focusout
. (This is to supplement a Kirby CMS form plugin that validates on submit only.)
The behaviour I'm trying to create is...
- When a required field is left empty, styling and message appear on
focusout
. - Styling and message remain visible until either (a) the field is in focus state again or (b) the field is populated.
- If a required field is 'refocussed', left blank and focus left again, the message and styling reappear.
My problem is that the input styling (red border-bottom) works as intended but the error message, which is based in the appendChild
function, does not. The error message only appears one at a time with the last focussed required field and I can't figure out how to get the message to stay in place the same way the styling does.
Because, in practice, forms will be created by content-managers using the CMS, I want the script to be vanilla JS and as generic as possible, avoiding targeting specific IDs. Instead, I'm trying to do this based on the required
attribute and the querySelectorAll
function. I have some variables to specity the error message text and a for
loop to target just the 'offending' fields that don't meet the criteria of an if
statement.
function reqdCheck() {
const reqdFields = document.querySelectorAll("main input[required], main textarea[required]");
const reqdPara = document.createElement("p");
const reqdText = document.createTextNode("This field is required.");
reqdPara.appendChild(reqdText);
for (const reqdField of reqdFields) {
reqdField.addEventListener ("focusout", function() {
if (reqdField.value == "" || reqdField.value == null) {
reqdField.style.borderBottom = "1px solid red";
reqdField.style.boxShadow = "0 1px 0 0 red";
reqdField.parentNode.appendChild(reqdPara);
}
else {
reqdField.style.borderBottom = "1px solid black";
reqdField.style.boxShadow = "none";
}
})
reqdField.addEventListener ("focusin", function() {
if (reqdField.value == "" || reqdField.value == null) {
reqdField.style.borderBottom = "1px solid orange";
reqdField.style.boxShadow = "0 1px 0 0 orange";
reqdField.parentNode.removeChild(reqdPara);
}
})
}
}
reqdCheck()
input:not([type="submit"]),
textarea {
border: none;
border-bottom: 1px solid black;
display: block;
width: calc(100% - 40px);
}
input:not([type="submit"]):focus,
textarea:focus {
outline: none;
border-bottom: 1px solid orange;
box-shadow: 0 1px 0 0 orange;
}
.field-group {
margin-bottom: 20px;
}
.field-group p {
color: red;
line-height: 1;
margin: 5px 0 0;
}
<main>
<form id="a2a64e93-2b60-4ad9-9445-ce61f5852594" action="#">
<div >
<label for="fname">First name:*</label><br>
<input type="text" id="fname" name="fname" placeholder="John" required>
</div>
<div >
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" placeholder="Doe">
</div>
<div >
<label for="company">Company:</label><br>
<input type="text" id="fname" name="company" placeholder="Acme Ltd">
</div>
<div >
<label for="email">Email:*</label><br>
<input type="email" id="email" name="email" placeholder="[email protected]" required>
</div>
<div >
<label for="email">Message:*</label><br>
<textarea id="message" name="message" placeholder="Message" required></textarea>
</div>
<input type="submit" value="Submit">
</form>
</main>
What am I missing to get the appendChild error message to stay visible as intended? Is there a more efficient way to achieve what I'm after? Thanks.
CodePudding user response:
The reason this happens is that you are creating only one p
element (the error text) and you want to append it multiple times. In order to resolve this, you should add all the reqdPara
logics inside the for loop. Take a look here:
function reqdCheck() {
const reqdFields = document.querySelectorAll(
'main input[required], main textarea[required]'
);
for (const reqdField of reqdFields) {
const reqdPara = document.createElement('p');
const reqdText = document.createTextNode('This field is required.');
reqdPara.appendChild(reqdText);
reqdField.addEventListener('focusout', function () {
if (reqdField.value === '' || reqdField.value === null) {
reqdField.style.borderBottom = '1px solid red';
reqdField.style.boxShadow = '0 1px 0 0 red';
reqdField.parentNode.appendChild(reqdPara);
} else {
reqdField.style.borderBottom = '1px solid black';
reqdField.style.boxShadow = 'none';
}
});
reqdField.addEventListener('focusin', function () {
if (reqdField.value == '' || reqdField.value == null) {
reqdField.style.borderBottom = '1px solid orange';
reqdField.style.boxShadow = '0 1px 0 0 orange';
reqdField.parentNode.removeChild(reqdPara);
}
});
}
}
reqdCheck();