Course lectures : 1 of 1

Processing / Loading Button

Last updated: 10th August 2013

loading buttonanimated buttonjquery button

Today we'll have a look at how to apply a processing or loading status to a button with the 3 animated dots, after the button has been clicked.

We are going to start with the basic page structure, which includes link to the jQuery on the content delivery network and core.css with core.js files.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Loading / Processing Button</title>
    <link
        href="./css/core.css"
        rel="stylesheet"
        type="text/css"
        >
</head>
<body>

<section id="wrapper">

    <button
        type="button"
        class="btnReplace button"
        data-label="Submit :button"
        >
        Submit :button
    </button>

    <input
        type="submit"
        class="btnReplace button"
        data-label="Submit :input"
        value="Submit :input"
        >

    <a
        href="#"
        class="btnReplace button"
        data-label="Submit :a"
        >
        Submit :a
    </a>

</section>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="./js/core.js"></script>
</body>
</html>

Each of our buttons has a data-label attribute that stores the original label value, which we will use later to put it back after the processing has completed.

Save the file as index.html and create two folders: css and js.

Inside of the newly created css folder create a new file called core.css and put the following structure inside of it:

* {
    padding: 0;
    margin: 0;
    border: none;
    font-weight: 100;
    font-family: Arial, sans-serif;
}
body {
    font-family: Arial, sans-serif;
    font-size: 12px;
    text-align: center;
    padding: 50px 0;
}
body, a {
    color: #333;
}
#wrapper {
    width: 800px;
    margin: 0 auto;
    text-align: left;
}
.button {
    line-height: 1em;
    background-color: #19839f;
    font-size: 12px;
    text-transform: uppercase;
    text-decoration: none;
    color: #fff !important;
    cursor: pointer;
    display: block;
    float: left;
    text-align: left;
    margin-right: 5px;
}
button.button {
    height: 27px;
    padding: 7px 15px;
}
button.button.disabled {
    width: 128px;
}
input.button {
    height: 27px;
    padding: 7px 15px;
}
input.button.disabled {
    width: 128px;
}
a.button {
    height: 19px;
    padding: 8px 15px 0 15px;
}
a.button.disabled {
    width: 90px;
}
.button.disabled {
    background-color: #1ac0dd;
    cursor: default;
}

As you can see from the above css structure, we have created a case for each button type - button, input or a. This is because of the way these are rendered in the browser.

Now create the file called core.js and save it in the js folder.

Inside of this file, start with the new object definition and a few, private properties:

function TemplateObject() {

    "use strict";

    var trigger = '.btnReplace',
        classDisabled = 'disabled',
        replaceString = 'Processing',
        numbOfDots = 3,
        timeSimulate = 5000;

}

Our new object is called TemplateObject() and we have defined the strict mode for it - this is to ensure that our code is spotless (free of any potential errors). Strict mode will automatically inform us if there are any problems with our javascript.

The properties we've defined represent the following:

  • trigger : trigger object name
  • classDisabled : css class, which will be added to the button while processing the request
  • replaceString : text label that will replace the default button's label
  • numbOfDots : the maximum number of dots we wish to append to the label
  • timeSimulate : the number of milliseconds that we wish our simulation to last once the button has been clicked

The first, private method inside of our new object is called processButtonDots():

function processButtonDots(obj, type, callback) {

    "use strict";

    var thisTime = 0,
        thisInitLength = replaceString.length,
        thisText = replaceString;

    var thisInterval = setInterval(function() {

        thisTime += 1000;

        if (thisText.length === parseInt((thisInitLength + numbOfDots), 10)) {

            thisText = replaceString + '.';

        } else {

            thisText = thisText + '.';

        }

        if (type == 'input') {

            obj.val(thisText);

        } else {

            obj.text(thisText);

        }

        if (thisTime > timeSimulate) {

            clearInterval(thisInterval);

            callback();

        }

    }, 1000);

}

The new methods takes 3 parameters:

  • obj : the actual button instance
  • type : type of the object, represented by the tag name - in our case it will be either button, input or a
  • callback : any callback function that we wish to call once the method has completed

We are first defining three variables:

  • thisTime : starting time, which will be incremented by 1000 milliseconds every second from within the setInterval() function
  • thisInitLength : the initial length of the string, which replaces the actual button label
  • thisText : with the value of the replacement label assigned to it

Next we set the thisInterval variable with the setInterval() function, that iterates every second (1000 milliseconds).
Each iteration of the setInterval function increases the thisTime value by 1000 milliseconds and checks whether the length of the label assigned to the thisText variable equals length of the replacement label plus the maximum number of dots. If it does then we reset the label to the replacement string plus just one dot - otherwise we concatenate string with another dot.

The next statement checks for the type of the object - if input, we change the value attribute, otherwise we replace the text between the opening and closing tag.

The last statement checks if thisTime variable is more than our set simulation time - usually here it would be based on the ajax request rather than hard coded time. If it's more than the set time, it clears interval and calls the callback function.

The next, private method is called processButton():

function processButton(obj) {

    "use strict";

    var thisType = obj.prop('tagName').toLowerCase(),
        thisString = obj.data('label');

    switch(thisType) {

        case 'input':

            obj.attr('disabled', true).addClass(classDisabled).val(replaceString);

            processButtonDots(obj, thisType, function() {

                obj.attr('disabled', false).removeClass(classDisabled).val(thisString);

            });

            break;

        case 'button':

            obj.attr('disabled', true).addClass(classDisabled).text(replaceString);

            processButtonDots(obj, thisType, function() {

                obj.attr('disabled', false).removeClass(classDisabled).text(thisString);

            });

            break;

        default:

            obj.addClass(classDisabled).text(replaceString);

            processButtonDots(obj, thisType, function() {

                obj.removeClass(classDisabled).text(thisString);

            });

            break;

    }

}

Here only one parameter is taken and it represents the trigger object. The two variable represent:

  • thisType : the type of the object - tagName property of the object
  • thisString : the value assigned to the data-label attribute of the trigger object

The switch statement then processes the request based on the type of the button.

If it's the input tag - it will apply the disabled="disabled" attribute to it, adds the class disabled and replaces the value attribute with the replacement label. It then calls the processButtonDots() method and as a callback (once everything has completed) it removes the disabled attribute, class and updates the value back to its original string.

For button it's pretty much the same, except for the fact that rather then using value attribute - it replaces the label in between the opening and closing tag using text() method.

The default case is applying to the a tag - which, like button, replaces the label between opening and closing tag, but does not apply the disabled attribute.

The last, public method is called clickButton():

this.clickButton = function() {

    "use strict";

    $(document).on('click', trigger, function(event) {

        event.preventDefault();

        if ( ! $(this).hasClass(classDisabled)) {

            processButton($(this));

        }

    });

};

This method simply binds the click event to our button and if the button does not already have the class disabled assigned to it - it calls the processButton() with the reference to the clicked object.

The last thing, in order to make it work is the instantiation of the object and call of our public method from within the document ready:

$(function() {

    var template = new TemplateObject();
    template.clickButton();

});

And that's it - you can test the project.

COMMENTS

If you'd like to include code example please wrap your code within <pre><code> tags.


<pre>
<code>
function fullName(firstName, lastName)
{
    return firstName + ' ' + lastName;
}
</code>
</pre>