Materialize Stepper

A small plugin that implements a stepper to Materializecss framework
GitHub version
GETTING STARTED

A stepper is a fundamental part of material design guidelines. It makes forms simplier and a lot of other stuffs.

Unfortunately, one of the best material design front-end frameworks we have today, Materilizecss, doesn't have an implemented stepper. So I decided to create one.

Here you going to find some demos and some code, but you can find the full documentation on GitHub.

I hope you like and help me improve it.

PREREQUISITES

Since it's an implementation to Materializecss framework, you'll need it:

  • Materializecss framework
  • Google Material Icons
INSTALLING
Direct download
NPM
npm install --save materialize-stepper
YARN
yarn add materialize-stepper
CDN

<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/materialize-stepper@3.1.0/dist/css/mstepper.min.css">
      

<!-- JS -->
<script src="https://unpkg.com/materialize-stepper@3.1.0/dist/js/mstepper.min.js">
      
Demos
Linear Stepper

Linear Stepper is the most common one. In it, you can't navigate freely between steps. It's step by step, obeying the validation function.

Example:


<ul class="stepper linear">...</ul>
         
  • Step 1
  • Step 2
  • Step 3
    Finish!
Non-linear Stepper

In the Non-Linear Stepper you can navigate freely between steps. You can also use the buttons for validation, but if the user wants to move arbitrarily around the steps, it's allowed by clicking on the steps instead of the buttons.

Example:


<ul class="stepper">...</ul>
                        
  • Step 1
  • Step 2
  • Step 3
    Finish!
Feedback Stepper

If you want, for example, to check if an e-mail exists in your DB, you can define a feedback function with data-feedback attribute in your next button.

When the user try to move forward, a loading screen will overlay the active step until you run destroyFeedback(true) (first param received by the function) with "true" in the first parameter to also skip the step (instead of just destroy the feedback preloader).

Example:


<button class="waves-effect waves-dark btn next-step" data-feedback="someFunction">CONTINUE</button>
<script>
   function someFunction(destroyFeedback) {
      // Do your stuff here
      // Call destroyFeedback() function when you're done
      // The true parameter will proceed to the next step besides destroying the preloader
      setTimeout(() => {
         destroyFeedback(true);
      }, 1000);
   }
</script>
         
  • Step 1
  • Step 2
  • Step 3
    Finish!
Horizontal Stepper

There is a horizontal version now (since 2.0). All the options above listed is working with it. And since version 2.1 horizontal steppers are responsive and turns to vertical from 992px (width) down:

To use it, you just need to add a ".horizontal" class to your primary "ul" tag:

Example:


<ul class="stepper horizontal">
   ...
</ul>
         
  • Step 1
  • Step 2
  • Step 3
    Finish!
USAGE

The HTML base is like this:


<ul class="stepper linear">
   <li class="step active">
      <div class="step-title waves-effect">E-mail</div>
      <div class="step-content">
         <!-- Your step content goes here (like inputs or so) -->
         <div class="step-actions">
            <!-- Here goes your actions buttons -->
            <button class="waves-effect waves-dark btn next-step">CONTINUE</button>
         </div>
      </div>
   </li>
</ul>
   

After that you'll need to initialize it through:


<script>
   var stepper = document.querySelector('.stepper');
   var stepperInstace = new MStepper(stepper, {
      // options
      firstActive: 0 // this is the default
   })
</script>
   
EXPLANATION

Every class explains very well it's function, so... Everything runs inside a ul with ".stepper" class:


<ul class="stepper linear">...</ul>
   

Each step runs inside a li tag with the class ".step":


<li class="step">...</li>
   

Inside each step there is two divs. The ".step-title", where you put the title of... guess what...


<div class="step-title waves-effect waves-dark">First step</div>
   

And the ".step-content", that holds the information:


<div class="step-content">...</div>
   

There's the ".step-actions" container inside step-content, which holds the buttons:


<div class="step-actions"></div>
   

And finally there's the buttons, which proceed (.next-step) or return (.previous-step):


<button class="waves-effect waves-dark btn next-step">CONTINUE</button>
<button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
   
OPTIONS
JS initialization setting(s)

If you are using linear stepper, clicking on the next and the previous step will work just like the buttons. Since 2.1 you can adjust some options. Here they are, with their respective defaults:


var stepper = document.querySelector('.stepper');
var stepperInstace = new MStepper(stepper, {
   // Default active step.
   firstActive: 0,
   // Allow navigation by clicking on the next and previous steps on linear steppers.
   linearStepsNavigation: true,
   // Auto focus on first input of each step.
   autoFocusInput: false,
   // Set if a loading screen will appear while feedbacks functions are running.
   showFeedbackPreloader: true,
   // Auto generation of a form around the stepper.
   autoFormCreation: true,
   // Function to be called everytime a nextstep occurs. It receives 2 arguments, in this sequece: stepperForm, activeStepContent.
   validationFunction: defaultValidationFunction, // more about this default functions below
   // Enable or disable navigation by clicking on step-titles
   stepTitleNavigation: true,
   // Preloader used when step is waiting for feedback function. If not defined, Materializecss spinner-blue-only will be used.
   feedbackPreloader: '<div class="spinner-layer spinner-blue-only">...</div>'
})
Form validation

As described in the previous topic, you can define a validation function to be called on every nextStep() in the stepper init options.

This function will be called with two parameters:


function validationFunction(stepperForm, activeStepContent) {
   // You can use the 'stepperForm' to valide the whole form around the stepper:
   someValidationPlugin(stepperForm);
   // Or you can do something with just the activeStepContent
   someValidationPlugin(activeStepContent);
   // Return true or false to proceed or show an error
   return true;
}

If you don't define anything, a default validation function will be used. This default function is the defaultValidationFunction. It's a simple form validation that works like this:


defaultValidationFunction(stepperForm, activeStepContent) {
   var inputs = activeStepContent.querySelectorAll('input, textarea, select');
   for (let i = 0; i < inputs.length; i++) if (!inputs[i].checkValidity()) return false;
   return true;
}
Horizontal and vertical

You can make your stepper horizontal just adding a ".horizontal" class to your primary "ul" tag. Since version 2.1 horizontal steppers are responsive and turns to vertical from 992px (width) down:


<ul class="stepper horizontal">...</ul>

For now, horizontal stepper contents doesn't have an automatic height. You can change the default size (458px) of your primary "ul" class with CSS:


ul.stepper.horizontal {
   min-height: 458px;
}

or inline:


<ul class="stepper horizontal" style="min-height:458px">...</ul>

IMPORTANT: THE HEIGHT OF THE ".stepper-content" TAG IS SUBTRACTED BY 84PX. SO, FOR EXAMPLE, IF YOU WANT YOUR CONTENT TO HAVE 400PX HEIGHT, YOU'LL NEED TO SET THE "min-height" OF YOUR PRIMARY "ul" TAG TO 484PX!

Linear and non-linear

If you want users to change between steps freely (without validations or the obligation to advance a step at a time), just remove .linear class from the primary ul:


<ul class="stepper">...</ul>
Form and inputs

If there is no "form" tag wrapping the ul, JS spawns it for the validate.js to work with the inputs (can be disabled). Since the primary funcion of stepper is to split some kind of form, for now, the only way to make a step required is to add "required" attribute to an input inside the .step-content container:


<input id="email" name="email" type="email" class="validate" required />
   

If the input is not valid, the icon will turn red until all required inputs become valid.

If you want to define your own attributes, just wrap the "ul" with a default "form", using any attributes you need:


<form action="youraction" method="yourmethod">
   <ul class="stepper linear">...</ul>
</form>
   

If you want to submit your stepper, just create a submit button with "submit" type and no feedback or "next/previous-step" class and if there's a wrapping form && a validation function, it'll call it before the submit (just return false to prevent it):


<button class="waves-effect waves-dark btn" type="submit">SUBMIT</button>
Step labels

You can add you own step labels by adding a "data-step-label" to your "step-titles" tags. Just like that:


<div data-step-label="OMG, they're so small and cute!" class="step-title waves-effect">Step title</div>
API
Navigate

There is three ways to navigate through steps.

  1. By clicking on them, if you are not using the ".linear" class. If you are, clicking on the next and the previous step will work just like the buttons (what you can disable with the JS initialization setting(s) listed above).
  2. By the buttons inside the steps:
    
    <!-- If you want the button to proceed, give it a .next-step class -->
    <button class="waves-effect waves-dark btn next-step">CONTINUE</button>
    <!-- If you want the button to return, give it a .previous-step class -->
    <button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
    <!-- If you want the button to submit the form, give it no additional classes and define type="submit" -->
    <button class="waves-effect waves-dark btn" type="submit">SUBMIT</button>
             
  3. By navigating programatically and, for that, there is three methods:
    
    // To proceed one step
    instace.nextStep(cb);
    // To go back one step
    instace.prevStep(cb);
    // To open arbitrarily a step
    // (the index is zero-based, so the first step is 0, not 1)
    instace.openStep(index, cb);
                
  • Step 1
  • Step 2
  • Step 3
    Finish!

Feedback function

There's a way to make the buttons run a function instead of proceeding, just add a data-feedback attribute with the function name to a ".next-step" classified button. Just like that:


<button class="waves-effect waves-dark btn next-step" data-feedback="checkEmailDB">CONTINUE</button>
<script>
   function checkEmailDB(destroyFeedback, form, activeStepContent) {
      // Do your stuff here
      // Call destroyFeedback() function when you're done
      // The true parameter will proceed to the next step besides destroying the preloader
      destroyFeedback(true);
   }
</script>
updateStepper method

If you want to the stepper to update the bindings (in case, for example, of AJAX loaded buttons), you can use updateStepper() method on the active instance.


instance.updateStepper();
resetStepper method

If you want to the stepper to it's original state (empty fields and options.firstActive), you can use resetStepper() method on the active instance. However, it can only be used on steppers wrapped in a form:


instance.resetStepper();
  • Step 1
  • Step 2
  • Step 3
    Finish!
Get Steps

You can get information about the steps by running:


var currentSteps = instace.getSteps()
   

And you'll get an object like this on var currentSteps:


{
   steps: HTMLCollection, // The current steps in the DOM
   active: {
      step: HTMLElement, // The current active step in the DOM
      index: number // The index of the active step
   }
}
wrongStep method

If you want to show an error on the active step, just call this method. This is also the method called by the stepper when a validationFunction returns 'false':


instance.wrongStep();

The error will only be dismissed when there's some change in the inputs.

  • Step 1
  • Step 2
  • Step 3
    Finish!
Dynamically adding/removing steps

If you want to dinamically add and activate/remove steps, you can do this:


<script>
   var elements;
   // The element can be a string:
   elements = '<div class="step">(...your step goes here...)</div>';
   // An array of strings:
   elements = ['<div class="step">(...your step goes here...)</div>', '<div class="step">(...your step goes here...)</div>'];
   // An HTMLCollection:
   elements = document.querySelectorAll('.steps-to-add');
   // Or an HTMLElement:
   elements = document.querySelector('.step-to-add');

   // Then you just need to run
   var addedSteps = instance.activateStep(elements, newStepsIndex);
   // And if you want, you can remove them afterwards:
   var removedSteps = instance.deactivateStep(addedSteps);
   // Or add them again :P
   var readdedSteps = instance.activateStep(removedSteps);
</script>
  • Step 1
  • Step 2
  • Step 3
    Finish!

Custom Events

There's currently 8 custom events in MStepper. All of them fires at the primary ul.stepper div:

  • stepchange: fires everytime a step is opened and one is closed.
  • stepopen: fires everytime a step is opened.
  • stepclose: fires everytime a step is closed.
  • nextstep: fires everytime the nextStep method is called.
  • prevstep: fires everytime the prevStep method is called.
  • steperror: fires everytime the wrongStep method is called.
  • feedbacking: fires everytime the activateFeedback method is called.
  • feedbackdestroyed: fires everytime the destroyFeedback method is called (and a feedback is actually destroyes).

Event Order

  1. Feedbacking
  2. Step Close
  3. Step Change
  4. Step Open
  5. Next / Previous Step
  6. Feedback Destroyed

Event Output

  • Step 1
  • Step 2
  • Step 3
    Finish!
Changelog

To get the change log, visit CHANGELOG.md page.
Ps: date format: d/m/y

Credits

Stepper is a plugin created by me, Igor Marcossi, inspired in MDL Stepper, a more powerful one that implements the same thing on Material Design Lite framework.

Donations
A penny for the poor dev

Just kidding, but the plugin surprisingly took me a bunch of time to make everything look pretty, so if I helped you, consider a donation. I'd be eternally grateful :)

What about a coffee?

I made even a cool donation button in a shape of a coffee cup, so why don't you buy me one? :D

Just click on the coffee cup below, c'mon, you can't resist!

Or you can send me some bitcoins :)

Just use this address: 1AE21GBjtQf5f7X3f1T2qdckgqcAhzxPdz