<!-- Stepper Form Content -->
<form id="stepper-form" role="form" action="" method="post">
<!-- Stepper Mobile Step Summary -->
<div class="stepper-mobile-summary">
Stap <span class="current-step">1</span> van <span class="step-count">6</span>
</div>
<!-- Stepper Steps -->
<ul class="stepper stepper-horizontal stepper-steps">
<!-- First Step -->
<li aria-label="Actieve stap" class="active" id="step-1">
<!-- Step Button -->
<a class="step-link" href="#step-content-1">
<span class="circle">1</span>
<span class="label">Eerste stap</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-1">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Basis informatie</strong></h3>
<div class="form-group md-form">
<input id="email" type="email" required="required" class="form-control validate" placeholder="Email">
<label for="email" data-error="error">Email</label>
</div>
<div class="form-group md-form">
<input id="username" type="text" required="required" class="form-control validate" placeholder="Gebruikersnaam">
<label for="username" data-error="required">Gebruikersnaam</label>
</div>
<button class="btn btn-primary nextBtn float-right" type="button">Volgende</button>
</div>
</div>
</li>
<!-- Second Step -->
<li class="disabled" id="step-2">
<!-- Step Button -->
<a class="step-link" href="#step-content-2">
<span class="circle">2</span>
<span class="label">Stap 2</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-2">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Persoonlijke data</strong></h3>
<div class="form-group md-form">
<input id="firstName" type="text" required="required" class="form-control validate" placeholder="Voornaam">
<label for="firstName" data-error="required">Voornaam</label>
</div>
<div class="form-group md-form mt-3">
<input id="secondName" type="text" required="required" class="form-control validate" placeholder="Achternaam">
<label for="secondName" data-error="required">Achternaam</label>
</div>
<button class="btn btn-primary backBtn float-left" type="button">Vorige</button>
<button class="btn btn-primary nextBtn float-right" type="button">Volgende</button>
</div>
</div>
</li>
<!-- Third Step -->
<li class="disabled" id="step-3">
<!-- Step Button -->
<a class="step-link" href="#step-content-3">
<span class="circle">3</span>
<span class="label">Stap 3</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-3">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Stap 3</strong></h3>
<div class="form-group md-form">
<input id="step3" type="text" required="required" class="form-control validate" placeholder="Stap 3 invoer">
<label for="step3" data-error="required">Stap 3 invoer</label>
</div>
<button class="btn btn-primary backBtn float-left" type="button">Vorige</button>
<button class="btn btn-primary nextBtn float-right" type="button">Volgende</button>
</div>
</div>
</li>
<!-- Fourth Step -->
<li class="disabled" id="step-4">
<!-- Step Button -->
<a class="step-link" href="#step-content-4">
<span class="circle">4</span>
<span class="label">Stap 4</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-4">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Stap 4</strong></h3>
<div class="form-group md-form">
<input id="step4" type="text" required="required" class="form-control validate" placeholder="Stap 4 invoer">
<label for="step4" data-error="required">Stap 4 invoer</label>
</div>
<button class="btn btn-primary backBtn float-left" type="button">Vorige</button>
<button class="btn btn-primary nextBtn float-right" type="button">Volgende</button>
</div>
</div>
</li>
<!-- Fifth Step -->
<li class="disabled" id="step-5">
<!-- Step Button -->
<a class="step-link" href="#step-content-5">
<span class="circle">5</span>
<span class="label">Stap 5</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-5">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Stap 5</strong></h3>
<div class="form-group md-form">
<input id="step5" type="text" required="required" class="form-control validate" placeholder="Stap 5 invoer">
<label for="step5" data-error="required">Stap 5 invoer</label>
</div>
<button class="btn btn-primary backBtn float-left" type="button">Vorige</button>
<button class="btn btn-primary nextBtn float-right" type="button">Volgende</button>
<!-- A submit button could be used at any point and will get triggered on step progression -->
<!-- <button class="btn btn-primary nextBtn float-right" type="submit">Submit</button> -->
</div>
</div>
</li>
<!-- Sixth Step -->
<li class="disabled" id="step-6">
<!-- Step Button -->
<a class="step-link" href="#step-content-6">
<span class="circle">6</span>
<span class="label">Laatste stap</span>
</a>
<!-- Step Content -->
<div class="step-content" id="step-content-6">
<div class="col-md-12">
<h3 class="font-weight-bold pl-0 my-4"><strong>Voltooid</strong></h3>
<h4 class="text-center font-weight-bold my-4">Registratie compleet!</h4>
</div>
</div>
</li>
</ul>
<!-- Stepper Mobile Navigation -->
<div class="stepper-mobile-navigation">
<a class="back-link float-left" href="#" aria-label="Vorige">
<span aria-hidden="true" class="mdi mdi-chevron-left"></span>
<span>Vorige</span>
</a>
<a class="next-link float-right" href="#" aria-label="Volgende">
<span>Volgende</span>
<span aria-hidden="true" class="mdi mdi-chevron-right"></span>
</a>
</div>
</form>
Based on the version from MDB:
https://mdbootstrap.comcomponents/bootstrap-steps-stepper/
And, Google’s Material design:
https://material.io/archive/guidelines/components/steppers.html#steppers-types-of-steps
Make sure that the step bar has the right aria-label
attributes applied to them. For the current step we expect
the tab to have aria-label="Actieve stap"
, and when the step is completed aria-label="Voltooide stap"
.
The stepper is designed to work with up to six steps and should appear only once per page.
The stepper allows both forward and backward progression once each step is valid.
Back buttons should only be included on the steps that need it. For instance, not step 1.
The step labels and form input can be changed freely to suit your needs.
The stepper uses a horizontal view on large width screen, vertical view on medium width screens and mobile view on small width screens.
As shown (commented out) within step 5 of the example, a submit button could be used at any point and will get triggered on step progression:
<button class="btn btn-primary nextBtn float-right" type="submit">Submit</button>
The submit button will post the form inputs, and could appear on any step.
The below JavaScript is required to use the Stepper component and should be placed in the Additional component(s) script
section as documented in How to use.
<script>
function areInputsValid(currentInputs, targetId, validateAllRequiredFields) {
var isValid = true;
for (var i = 0; i < currentInputs.length; i++) {
$(currentInputs[i]).addClass("valid");
if (!currentInputs[i].validity.valid){
isValid = false;
// Only show a required input is invalid for the current input or when validating all required
if (validateAllRequiredFields || currentInputs[i].id === targetId) {
$(currentInputs[i]).addClass("invalid");
}
}
}
return isValid;
}
function updateBackButtons(navigationStep, mobileBackBtn) {
if (navigationStep.find('.backBtn').length > 0) { // Check if step has back button
mobileBackBtn.show();
} else {
mobileBackBtn.hide();
}
}
function isValidStep(stepNumber, validSteps) {
return $.inArray(stepNumber, validSteps) > -1;
}
function setNavigationItemStatuses(navigationListItems, navigationStep, currentStepNumber, stepCount, validSteps) {
navigationStep.prevAll().removeClass('disabled').addClass('completed').removeClass('active').attr('aria-label', 'Voltooide stap');
navigationStep.removeClass('disabled').removeClass('completed').addClass('active').attr('aria-label', 'Actieve stap');
var currentStepIsValid = isValidStep(currentStepNumber, validSteps);
if (!currentStepIsValid) {
navigationStep.nextAll().addClass('disabled').removeClass('active').removeClass('completed').removeAttr('aria-label');
return;
}
navigationStep.nextAll().removeClass('active');
var previousStepValid = true;
for (var i = currentStepNumber + 1; i <= stepCount; i++) {
if (isValidStep(i, validSteps) && previousStepValid) {
navigationListItems.eq(i - 1).parent().removeClass('disabled').addClass('completed').attr('aria-label', 'Voltooide stap');
} else if (previousStepValid) {
navigationListItems.eq(i - 1).parent().removeClass('disabled').removeClass('completed').removeAttr('aria-label');
} else {
navigationListItems.eq(i - 1).parent().addClass('disabled').removeClass('completed').removeAttr('aria-label');
}
previousStepValid = isValidStep(i, validSteps) && previousStepValid;
}
navigationStep.next().removeClass('disabled');
}
function updateButtons(
navigationListItems,
navigationStep,
allBackBtn,
allNextBtn,
mobileBackBtn,
mobileNextBtn,
currentStepNumber,
stepCount,
validSteps
) {
setNavigationItemStatuses(navigationListItems, navigationStep, currentStepNumber, stepCount, validSteps);
mobileNextBtn.show();
if (navigationStep.find('.nextBtn').length > 0) { // Check if step has a next button
updateBackButtons(navigationStep, mobileBackBtn);
} else {
mobileNextBtn.hide();
}
}
function updateStepStatus(
navigationListItems,
navigationStep,
allBackBtn,
allNextBtn,
mobileBackBtn,
mobileNextBtn,
currentStepNumber,
stepCount,
validSteps
) {
updateButtons(
navigationListItems,
navigationStep,
allBackBtn,
allNextBtn,
mobileBackBtn,
mobileNextBtn,
currentStepNumber,
stepCount,
validSteps
);
$('.current-step').text(currentStepNumber);
$('.step-count').text(stepCount);
}
// Stepper Form
$(document).ready(function () {
var navigationListItems = $('.stepper-steps li a'),
allContent = $('.step-content'),
allBackBtn = $('.backBtn'),
allNextBtn = $('.nextBtn'),
mobileBackBtn = $('.back-link'),
mobileNextBtn = $('.next-link'),
activeStep = $('.stepper-steps li.active a'),
stepperForm = $('#stepper-form'),
stepperFormInputs = $('#stepper-form :input'),
validSteps = [];
allContent.hide();
stepperFormInputs.on('change keyup paste', function(event) {
var currentStepContent = $(this).closest(".step-content"),
currentStep = $('.stepper-steps li a[href="#' + currentStepContent.attr("id") + '"]').parent(),
currentStepNumber = parseInt(currentStepContent.attr("id").substring(13)),
currentInputs = currentStepContent.find("input");
currentStep.removeClass('disabled');
var stepPositionInArray = $.inArray(currentStepNumber, validSteps);
var stepIsValid = isValidStep(currentStepNumber, validSteps);
if (areInputsValid(currentInputs, event.target.id, false)) {
if (!stepIsValid) validSteps.push(currentStepNumber);
} else {
if (stepIsValid) validSteps.splice(stepPositionInArray, 1);
}
updateStepStatus(
navigationListItems,
currentStep,
allBackBtn,
allNextBtn,
mobileBackBtn,
mobileNextBtn,
currentStepNumber,
allContent.length,
validSteps
);
});
navigationListItems.click(function (event) {
event.preventDefault();
var $target = $($(this).attr('href')),
$item = $(this);
if (!$item.hasClass('disabled')) {
if ($item.parent().prev().find('button[type="submit"]').length) {
stepperForm.submit();
}
allContent.hide();
$target.show();
$target.find('input:eq(0)').focus();
updateStepStatus(
navigationListItems,
$item.parent(),
allBackBtn,
allNextBtn,
mobileBackBtn,
mobileNextBtn,
parseInt($item.attr('href').substring(14)),
allContent.length,
validSteps
);
}
});
mobileBackBtn.add(allBackBtn).click(function(){
var currentStepContent = $('.stepper-steps li.active').children('.step-content'),
previousStep = $('.stepper-steps li a[href="#' + currentStepContent.attr("id") + '"]').parent().prev().children("a");
previousStep.trigger('click');
});
mobileNextBtn.add(allNextBtn).click(function(){
var currentStepContent = $('.stepper-steps li.active').children('.step-content'),
nextStep = $('.stepper-steps li a[href="#' + currentStepContent.attr("id") + '"]').parent().next().children("a"),
currentInputs = currentStepContent.find("input");
if (areInputsValid(currentInputs, null, true)) {
nextStep.trigger('click');
}
});
activeStep.trigger('click');
$(window).keydown(function(event){
if((event.keyCode == 13) && !$(event.target).is(
'.back-link, .backBtn, .next-link, .nextBtn, .step-link'
)) {
event.preventDefault();
return false;
}
});
});
</script>
id
, href
, aria-controls
and/or aria-labelledby
for your use-case, be sure to rename all occurrencesaria-label
telling the status of the steparia-label="Voltooide stap"
aria-label="Actieve stap"