Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jquery automatic loading gif and button disable on submit click

Is it possible to automate the showing/hiding of a ajax loading gif, and the disabling/enabling of the submit button at the same time? (when the submit button is a styled < a > not a input type=submit)

Currently when submitting I do this:

$("#save_button_id").click(function () {
        if ($('#save_button_id').hasClass('ui-state-disabled')) return false;
        Save();
});

function Save() {
StartAjax($("#save_button_id"));
$.ajax({
        success: function (data) {
            EndAjax($("#save_button_id"));
            // etc...
        },
        error: function (xhr, status, error) {
            EndAjax($("#save_button_id"));
            // etc ...
        }
});
}

function StartAjax(button) {
    DisableButtonClick(button);
    $("#ajaxLoad").show();
}

function EndAjax(button) {
    EnableButtonClick(button);
    $("#ajaxLoad").hide();
}

I've seen a few places talk about how to use .ajaxStart() to automatically show the loading gif, but is it possible to also find a reference to the button (a styled < a > tag) that was clicked, and automatically disable/enable that as well?

The point of this is to not have to manually type in Start/EndAjax every time, and to make sure the app is consistent everywhere.

EDIT

None of the answers so far offer automation - any solution (like my current one above) where you have to manually type start/end before and after every single $.ajax() causes a maintenance problem: Its easy to forget to place the start/end next to some $.ajax() calls, and if you want to change how it works later you need to go through every single one to make the change.

EDIT 2 - to clarify point re the .delegate() suggestion

You said "You can attach your event handler to any element" - but I want to attach it to every button element (DRY!): so I've modified the 1st part of your suggestion to be:

$('div#buttons a.ui-icon').each(function(index) {
    $(this).ajaxStart(function() {
        $("#ajaxLoad").show();
    });
});

This solves the first problem, which is how to show the loading .gif for any button, without having to repetitively type "$("#ajaxLoad").show()" everywhere there is an $.ajax() call.

The next part is how to disable any button when it is clicked (again with no repetitive code) - you suggested .delegate(). But in your example every button click will call the Save() method. (I changed the selector to match my html)

$('div#buttons a.ui-icon').delegate('button:not(.ui-state-disabled)', 'click', function() {
   $(this).toggleClass('ui-state-disabled');
   Save();  // this line needs replacing with a call to $(this).something
});

The problem with this is that $(this) is not always the save button (the selector returns ALL the buttons), it might be the delete button or the cancel button or etc. So calling $(this).toggleClass is fine, but calling Save() means you are calling the wrong function. All these buttons already have a .click method:

$("#SaveButton").click(function () {
    Save();
});

$("#DeleteButton").click(function () {
    Delete();
});

So this is the original click function that needs to be called where it says $(this).something above. At this point it should be calling the original click - or perhaps it is more correct to say it should then bubble up to the original .click. The .delegate must be more generic, and the original .click will provide the specific implementation.

like image 774
JK. Avatar asked May 20 '10 23:05

JK.


2 Answers

It actually turned out to be very straightforward: I've included this in my helper.js file that is run for every page:

$(document).ready(function () {
    $('div#buttons a.fm-button').each(function (index) {
        $(this).ajaxStart(function () {
            $("#ajaxLoad").show();
            DisableButtonClick($(this));
        });
        $(this).ajaxStop(function () {
            $("#ajaxLoad").hide();
            EnableButtonClick($(this));
        });
    });
});

Now whenever any of the ajax-y buttons are clicked on any page, the buttons are disabled and the ajax loading gif is shown. When the ajax call ends they are returned to their normal state. And all done with out repetitively typing code every time .ajax() is called.

like image 83
JK. Avatar answered Sep 23 '22 19:09

JK.


You can attach your event handler to any element; so for your save button:

$(this).ajaxStart(function() {
  $("#ajaxLoad").show();
});

Now, you can listen for the click event for each button on your page and call the ajax function from that element:

$('div').delegate('button:not(.ui-state-disabled)', 'click', function() {
   $(this).toggleClass('ui-state-disabled');
   Save();
});

The $.delegate() function listens to click events for each element that does not have the .ui-state-disabled class. When an event is fired, it will toggle the class of that button to disabled, call your Save function and the $.ajaxStart() event will fire. That event will show your animated ajax gif.

I have assumed that your buttons are all contained in a div for this example. You many want to modify the code so that $('div').delegate... actually specifies the containing element of your buttons. I am also assuming you're using jQuery 1.4.2.

You should be able to use the $.ajaxStop() function to revert everything back again.

like image 27
Phil.Wheeler Avatar answered Sep 26 '22 19:09

Phil.Wheeler