Advertisements
I have a multistep form with two fieldsets, containing two required inputs each. I want to validate only the first fieldset when the user wants to continue to the second one.
However, with my current approach, the entire form is validated, although I call checkValidity()
only on the input elements of the first fieldset. As a result, when progressing to the second fieldset, users are greeted with invalidated input fields and warning tooltips.
What am I doing wrong? How can I validate the fieldsets separately? I’ve found approaches for jQuery and JSP, but not for plain ES6.
Here is my code.
let fieldsetElements;
function validateFieldset(step) {
const inputElements = document.querySelectorAll(`fieldset.active input`);
return !Array.from(inputElements).find((element) => !element.checkValidity());
}
function goToStep(step) {
Array.from(fieldsetElements)
.filter((element) => element.classList.contains("active"))
.forEach((element) => element.classList.remove("active"));
fieldsetElements[step - 1].classList.add("active");
}
function continueToStep(nextStep) {
const currentStep = nextStep - 1;
const fieldsetValid = validateFieldset(currentStep);
if (fieldsetValid) {
goToStep(nextStep);
}
}
document.addEventListener("DOMContentLoaded", () => {
fieldsetElements = document.getElementsByTagName("fieldset");
goToStep(1);
});
fieldset {
display: none;
}
fieldset.active {
display: block;
}
<form>
<fieldset>
<legend>Your name</legend>
<p>
<label for="prename">Prename (required)</label><br />
<input type="text" id="prename" name="prename" required />
</p>
<p>
<label for="surname">Surname (required)</label><br />
<input type="text" id="surname" name="surname" required />
</p>
<p>
<button onclick="continueToStep(2)">Continue</button>
</p>
</fieldset>
<fieldset>
<legend>Your contact information</legend>
<p>
<label for="email">Email (required)</label><br />
<input type="email" id="email" name="email" required />
</p>
<p>
<label for="phone">Phone</label><br />
<input type="tel" id="phone" name="phone" />
</p>
<button onclick="goToStep(1)">Go back</button>
<button type="submit">Submit</button>
</fieldset>
</form>
>Solution :
The issue is that without a type
attribute all button
tags trigger form validation. Add type="button"
to your buttons and it should work as you expect.
let fieldsetElements;
function validateFieldset(step) {
const inputElements = document.querySelectorAll(`fieldset.active input`);
return !Array.from(inputElements).find((element) => !element.checkValidity());
}
function goToStep(step) {
Array.from(fieldsetElements)
.filter((element) => element.classList.contains("active"))
.forEach((element) => element.classList.remove("active"));
fieldsetElements[step - 1].classList.add("active");
}
function continueToStep(nextStep) {
const currentStep = nextStep - 1;
const fieldsetValid = validateFieldset(currentStep);
if (fieldsetValid) {
goToStep(nextStep);
}
}
document.addEventListener("DOMContentLoaded", () => {
fieldsetElements = document.getElementsByTagName("fieldset");
goToStep(1);
});
fieldset {
display: none;
}
fieldset.active {
display: block;
}
<form>
<fieldset>
<legend>Your name</legend>
<p>
<label for="prename">Prename (required)</label><br />
<input type="text" id="prename" name="prename" required />
</p>
<p>
<label for="surname">Surname (required)</label><br />
<input type="text" id="surname" name="surname" required />
</p>
<p>
<button type="button" onclick="continueToStep(2)">Continue</button>
</p>
</fieldset>
<fieldset>
<legend>Your contact information</legend>
<p>
<label for="email">Email (required)</label><br />
<input type="email" id="email" name="email" required />
</p>
<p>
<label for="phone">Phone</label><br />
<input type="tel" id="phone" name="phone" />
</p>
<button type="button" onclick="goToStep(1)">Go back</button>
<button type="submit">Submit</button>
</fieldset>
</form>