Buttons With Built-in Progress Meters

Progress bars have become quite popular lately, and there are lots of plugins that can help you add one to your site. But how do you make one yourself? The problem is that there are plenty of implementations already, so in this tutorial, we are going to code something different – buttons that have built in progress meters.

They will be perfect for displaying progress while submitting forms or loading content via AJAX. They will also use CSS3 styles and transitions to make them easy to customize.

The HTML

In the first section of the tutorial, we will write the HTML markup. It comes in the form of a standard HTML5 document which includes two additional resources that we will discuss later – the styles.css stylesheet and the script.js JavaScript file. Additionally, I have included the jQuery library, and the Raleway font from Google web fonts.

The markup is pretty simple. The progress buttons are defined as regular hyperlinks. In order to be recognized by the plugin and turned into buttons with built-in progress bars, they need to have the .progress-button class. Buttons can also be configured by setting three data-* attributes:

data-type specifies what type of progress bars will be shown. Currently three types are supported: background-horizontal (the default), background-bar and background-vertical.

data-loading specifies the text that is shown while the progress bar is moving. The default value is Loading..

data-finished holds the text that is set on the button when the progress is done. The default value is Done!

If you omit an attribute, the default value will be used.

Buttons With Built-in Progress Meters

The jQuery Code

In this part of the tutorial, we are going to write the JavaScript and jQuery code to make the buttons work. The code is organized as 6 jQuery plugins that share a common name – progressInitialize, progressStart, progressIncrement, progressTimed, progressSet and progressFinish. I have provided plenty of comments in the code, so you can dig right in:

assets/js/script.js

$(document).ready(function(){
// Convert all the links with the progress-button class to
// actual buttons with progress meters.
// You need to call this function once the page is loaded.
// If you add buttons later, you will need to call the function only for them.
$('.progress-button').progressInitialize();
// Listen for clicks on the first three buttons, and start
// the progress animations
$('#submitButton').click(function(e){
e.preventDefault();
// This function will show a progress meter for
// the specified amount of time
$(this).progressTimed(2);
});
$('#actionButton').click(function(e){
e.preventDefault();
$(this).progressTimed(2);
});
$('#generateButton').one('click', function(e){
e.preventDefault();
// It can take a callback
var button = $(this);
button.progressTimed(3, function(){
// In this callback, you can set the href attribute of the button
// to the URL of the generated file. For the demo, we will only
// set up a new event listener that alerts a message.
button.click(function(){
alert('Showing how a callback works!');
});
});
});
// Custom progress handling
var controlButton = $('#controlButton');
controlButton.click(function(e){
e.preventDefault();
// You can optionally call the progressStart function.
// It will simulate activity every 2 seconds if the
// progress meter has not been incremented.
controlButton.progressStart();
});
$('.command.increment').click(function(){
// Increment the progress bar with 10%. Pass a number
// as an argument to increment with a different amount.
controlButton.progressIncrement();
});
$('.command.set-to-1').click(function(){
// Set the progress meter to the specified percentage
controlButton.progressSet(1);
});
$('.command.set-to-50').click(function(){
controlButton.progressSet(50);
});
$('.command.finish').click(function(){
// Set the progress meter to 100% and show the done text.
controlButton.progressFinish();
});
});
// The progress meter functionality is available as a series of plugins.
// You can put this code in a separate file if you wish to keep things tidy.
(function($){
// Creating a number of jQuery plugins that you can use to
// initialize and control the progress meters.
$.fn.progressInitialize = function(){
// This function creates the necessary markup for the progress meter
// and sets up a few event listeners.
// Loop through all the buttons:
return this.each(function(){
var button = $(this),
progress = 0;
// Extract the data attributes into the options object.
// If they are missing, they will receive default values.
var options = $.extend({
type:'background-horizontal',
loading: 'Loading..',
finished: 'Done!'
}, button.data());
// Add the data attributes if they are missing from the element.
// They are used by our CSS code to show the messages
button.attr({'data-loading': options.loading, 'data-finished': options.finished});
// Add the needed markup for the progress bar to the button
var bar = $('<span class="tz-bar ' + options.type + '">').appendTo(button);
// The progress event tells the button to update the progress bar
button.on('progress', function(e, val, absolute, finish){
if(!button.hasClass('in-progress')){
// This is the first progress event for the button (or the
// first after it has finished in a previous run). Re-initialize
// the progress and remove some classes that may be left.
bar.show();
progress = 0;
button.removeClass('finished').addClass('in-progress')
}
// val, absolute and finish are event data passed by the progressIncrement
// and progressSet methods that you can see near the end of this file.
if(absolute){
progress = val;
}
else{
progress += val;
}
if(progress >= 100){
progress = 100;
}
if(finish){
button.removeClass('in-progress').addClass('finished');
bar.delay(500).fadeOut(function(){
// Trigger the custom progress-finish event
button.trigger('progress-finish');
setProgress(0);
});
}
setProgress(progress);
});
function setProgress(percentage){
bar.filter('.background-horizontal,.background-bar').width(percentage+'%');
bar.filter('.background-vertical').height(percentage+'%');
}
});
};
// progressStart simulates activity on the progress meter. Call it first,
// if the progress is going to take a long time to finish.
$.fn.progressStart = function(){
var button = this.first(),
last_progress = new Date().getTime();
if(button.hasClass('in-progress')){
// Don't start it a second time!
return this;
}
button.on('progress', function(){
last_progress = new Date().getTime();
});
// Every half a second check whether the progress
// has been incremented in the last two seconds
var interval = window.setInterval(function(){
if( new Date().getTime() > 2000+last_progress){
// There has been no activity for two seconds. Increment the progress
// bar a little bit to show that something is happening
button.progressIncrement(5);
}
}, 500);
button.on('progress-finish',function(){
window.clearInterval(interval);
});
return button.progressIncrement(10);
};
$.fn.progressFinish = function(){
return this.first().progressSet(100);
};
$.fn.progressIncrement = function(val){
val = val || 10;
var button = this.first();
button.trigger('progress',[val])
return this;
};
$.fn.progressSet = function(val){
val = val || 10;
var finish = false;
if(val >= 100){
finish = true;
}
return this.first().trigger('progress',[val, true, finish]);
};
// This function creates a progress meter that
// finishes in a specified amount of time.
$.fn.progressTimed = function(seconds, cb){
var button = this.first(),
bar = button.find('.tz-bar');
if(button.is('.in-progress')){
return this;
}
// Set a transition declaration for the duration of the meter.
// CSS will do the job of animating the progress bar for us.
bar.css('transition', seconds+'s linear');
button.progressSet(99);
window.setTimeout(function(){
bar.css('transition','');
button.progressFinish();
if($.isFunction(cb)){
cb();
}
}, seconds*1000);
};
})(jQuery);

progressInitialize sets up an event listener for the progress custom event that the other functions call whenever the meter needs to be updated. Thanks to the custom event, we can have entirely independent functions like progressStart, which manages its own timer and state – progresInitialize need not know about progressStart.

The other important thing is that we are setting two special classes on the buttons – .in-progress while the progress meter is moving, and .finished when it’s ready. They are used to update the text of the buttons as you will see in the next section.

The CSS

I mentioned that we are setting two CSS classes on the buttons – .in-progress and .finished. But how does adding one of these classes change the text of the button? Simple – we are using a CSS trick that involves the CSS3 attr operator, which when combined with content, can set the text of a :before or :after pseudo element to that of the attribute of the element. It will become clearer once you see it for yourself (lines 44-52):

The rest of the code styles the buttons and the built-in progress meter. In styles.css I have also included two additional color themes and some other rules that are not given here, but which you can see by browsing the source yourself.

We’re Done!

The code we wrote today is ready for customizations. Simply open styles.css in your favorite code editor and change the colors, fonts or styles so they match your site. Change the text by editing the HTML and the data attributes. Or you can even improve the code with some cool new feature. In this case be sure to share it in the comment section :)

Presenting Bootstrap Studio

a revolutionary tool that developers and designers use to createbeautiful interfaces using the Bootstrap Framework.

Hi there!
This is awesome. I use it for my school project. But I have a question here.

I use it to show progress of querying postgresql with php. I have some steps there. For 1 step, I make 1 progress button. So when I click the first step button, it works. But after that, when I click the second step button, it doesnt work. So whats wrong?