March 07, 2014 Jonathan Beurel 7 min read

Bootstrap was created at Twitter in mid-2010 by @mdo and @fat. It provides a lot of UI plugins easy to implement. You've probably already used it but have you looked at how it worked? Together, we will try to make another plugin using the same best practices as Twitter. Through this tutorial I will introduce some JavaScript concepts like prototype, data-api or object-oriented programming.

A great way to learn is to work on a concrete example so you will build a jQuery plugin in order to manipulate a Bootstrap progressbar.

See the Pen Bootstrap Progressbar by Jonathan (@jbeurel) on CodePen.

Git: The code used in this tutorial is available on GitHub. You can follow the process step by step by using git tags:

Clone the bootstrap-progressbar-tuto repository: git clone https://github.com/jbeurel/bootstrap-progressbar-tuto.git Change your current directory to bootstrap-progressbar-tuto cd bootstrap-progressbar-tuto This is your working directory for the rest of this tutorial You can already see the rendering by opening the index.html file in your web browser

step-0: The DOM

Git: This command resets your working directory to the step 0 of the tutorial:

git checkout -f step-0

Let's begin by using basically the Bootstrap Progressbar:

<html> <head> <title>Bootstrap Progress Bar</title> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> </head> <body> <div class="container"> <h1>Bootstrap Progress Bar</h1> <div class="progress"> <div class="progress-bar" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%;"> <span class="sr-only">40% Complete</span> </div> </div> </div> <script src="//code.jquery.com/jquery.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> </body> </html>

You've created a simple template that includes Bootstrap assets and displays the static progressbar example provided by Bootstrap

Git: Code Diff

step-1 - The jQuery plugin

Git: Reset the workspace to step 1:

git checkout -f step-1

Next step: Javascript!

Create a new js/progressbar.js file with this code:

!function ($) { "use strict"; // PROGRESSBAR CLASS DEFINITION // ============================ var Progressbar = function (element) { this.$element = $(element); } // PROGRESSBAR PLUGIN DEFINITION // ============================= $.fn.progressbar = function (option) { return this.each(function () { var $this = $(this), data = $this.data('jbl.progressbar'); if (!data) $this.data('jbl.progressbar', (data = new Progressbar(this))); }) }; }(window.jQuery);

What is the code doing?

IIFE (Immediately-Invoked Function Expression): !function ($) { }(window.jQuery); The ! converts the function declaration to a function expression and allows to invoke it immediatly by adding the (window.jQuery) parentheses. You can find more information on this Stackoverflow thread.

Class definition var Progressbar = function (element) { this.$element = $(element); } This creates a Progressbar class that contains the DOM element that you'll be able to manipulate later.

jQuery plugin definition $.fn.progressbar = function () { return this.each(function () { var $this = $(this), data = $this.data('jbl.progressbar'); if (!data) $this.data('jbl.progressbar', (data = new Progressbar(this))); }) }; $.fn is an alias of the jQuery.prototype . You can use it to extend jQuery with your own progressbar function. You use each DOM element selected by a jQuery selector as a Singleton data storage to store an instance of a Progressbar object.

Git: Code Diff

step-2 - The prototype

Git: Reset the workspace to step 2:

git checkout -f step-2

The next step is to add an update function to your Progressbar to change its value. Methods can be added to a JS class by defining functions as children of the prototype class attribute (see Object.prototype). In this way, add this code to your js/progressbar.js file:

Progressbar.prototype.update = function (value) { var $div = this.$element.find('div'); var $span = $div.find('span'); $div.attr('aria-valuenow', value); $div.css('width', value + '%'); $span.text(value + '% Complete'); }

This function changes the values of the aria-valuenow attribute, CSS width and the text. Using it will update all this elements at once. Let's improve the plugin definition to use your newly created function:

$.fn.progressbar = function (option) { return this.each(function () { var $this = $(this), data = $this.data('jbl.progressbar'); if (!data) $this.data('jbl.progressbar', (data = new Progressbar(this))); if (typeof option == 'number') data.update(option); }) };

Add option parameter to your progressbar function and use it to call the update function. Try out this command in the browser console to test your code:

$('#myProgressbar').progressbar(20)

Nice! You can see the Progressbar change.

Git: Code Diff

step-3 - The use case

Git: Reset the workspace to step 3:

git checkout -f step-3

Your plugin is technically working, but how do you want to use it ? The Bootstrap style answer is to call it from your HTML markup. Update the index.html file by adding this HTML:

<p> <button data-toggle="progressbar" data-target="#myProgressbar" data-value="0" class="btn btn-default">0%</button> <button data-toggle="progressbar" data-target="#myProgressbar" data-value="10" class="btn btn-default">10%</button> <button data-toggle="progressbar" data-target="#myProgressbar" data-value="30" class="btn btn-default">30%</button> <button data-toggle="progressbar" data-target="#myProgressbar" data-value="75" class="btn btn-default">75%</button> <button data-toggle="progressbar" data-target="#myProgressbar" data-value="100" class="btn btn-default">100%</button> </p>

data-toggle="progressbar" allows to mark this button as a Progressbar toggler.

allows to mark this button as a Progressbar toggler. data-target="#myProgressbar" determines which Progressbar is updated by this button.

determines which Progressbar is updated by this button. data-value="0" defines next value of the Progressbar.

This HTML is the contract that you will try to honor in the next step.

Git: Code Diff

step-4 - The DATA-API

Git: Reset the workspace to step 4:

git checkout -f step-4

For each click on a button, you will use the jQuery object data-api to collect the information needed by the plugin:

// PROGRESSBAR DATA-API // ==================== $(document).on('click', '[data-toggle="progressbar"]', function (e) { var $this = $(this); var $target = $($this.data('target')); var value = $this.data('value'); e.preventDefault(); $target.progressbar(value); });

This listener is added to the progressbar.js file and listens to all the buttons in the application containing the data-toggle="progressbar" attribute. Finally, update the Progressbar with the correct value.

Git: Code Diff

step-5 - The optional functions

Git: Reset the workspace to step 5:

git checkout -f step-5

Your Progressbar jQuery plugin works! Last exercise to improve it. How could you use this new button?

<button data-toggle="progressbar" data-target="#myProgressbar" data-value="reset" class="btn btn-default">Reset</button>

typeof javascript function allows to treat differently an integer and a string passed to the the data-value attribute and calls the correct function:

if (typeof option == 'string') data[option]();

This line added to the plugin definition allows to call this function:

Progressbar.prototype.reset = function () { this.update(0); }

The plugin definition is now ready to accept some improvements. You can simply add a new function to the plugin and call it by passing its name to the data-value attribute. It has never been easier to add a finish function. In the progressbar.js file:

Progressbar.prototype.finish = function () { this.update(100); }

In the template:

<button data-toggle="progressbar" data-target="#myProgressbar" data-value="finish" class="btn btn-default">Finish</button>

Works like a charm, doesn't it?

Git: Code Diff

Conclusion

This code, inspired by Bootstrap plugins, was to show you how to create a jQuery plugin using a Javascript object, its prototype and communicate between DOM element through the jQuery data-api.

I hope you read this tutorial with interest. Don't hesitate to comment for any questions or suggestions!