424

In Prototype I can show a "loading..." image with this code:

var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, 
onLoading: showLoad, onComplete: showResponse} );

function showLoad () {
    ...
}

In jQuery, I can load a server page into an element with this:

$('#message').load('index.php?pg=ajaxFlashcard');

but how do I attach a loading spinner to this command as I did in Prototype?

Danil Speransky
  • 27,720
  • 3
  • 57
  • 71
Edward Tanguay
  • 176,854
  • 291
  • 683
  • 1,015

24 Answers24

806

There are a couple of ways. My preferred way is to attach a function to the ajaxStart/Stop events on the element itself.

$('#loadingDiv')
    .hide()  // Hide it initially
    .ajaxStart(function() {
        $(this).show();
    })
    .ajaxStop(function() {
        $(this).hide();
    })
;

The ajaxStart/Stop functions will fire whenever you do any Ajax calls.

Update: As of jQuery 1.8, the documentation states that .ajaxStart/Stop should only be attached to document. This would transform the above snippet to:

var $loading = $('#loadingDiv').hide();
$(document)
  .ajaxStart(function () {
    $loading.show();
  })
  .ajaxStop(function () {
    $loading.hide();
  });
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
nickf
  • 499,078
  • 194
  • 614
  • 709
  • 51
    That's probably too global for one simple load into a DIV. – montrealist Jul 29 '09 at 17:05
  • 371
    too global? how many different "please wait" messages do you want? – nickf Aug 01 '09 at 17:59
  • 23
    this way unfortunately you can't control loading div positioning in case you don't want to just show a modal loading window, but show it near the element waiting for ajax content to be loaded in... – glaz666 Oct 17 '10 at 11:44
  • 10
    ajaxStart and ajaxStop are jQuery events so you can namespace them: http://stackoverflow.com/questions/1191485/how-to-call-ajaxstart-on-specific-ajax-calls/1212728#1212728 http://docs.jquery.com/Namespaced_Events – David Xia Apr 16 '11 at 22:57
  • 2
    This is very clear, +1! @glaz666 - That is not true. It is *all* about how *you* configure it. For example: `.ajaxStart(function(e) { CallFunctionWithTargettedElement(e.currentTarget); })` will call a function where the argument is the element which initiated the ajax call. Note that this is just an example with no handling for if the ajax was called from a script without an event handler or any other error checking whatsoever. It is merely to show that there are ways of controlling the positioning. The function called could place an absolute element over the calling element. – Travis J Sep 24 '12 at 18:53
  • This works for me only the first time I trigger the query, second and next times the animation does not show. any ideas? – deckoff Oct 29 '12 at 17:27
  • 2
    @deckoff it only triggers when an ajax request is started and there were no ajax requests still active. – nickf Oct 29 '12 at 22:04
  • 10
    If using jQuery >= 1.9, attach the ajaxStart and ajaxStop events to $(document). http://jquery.com/upgrade-guide/1.9/#ajax-events-should-be-attached-to-document – Domenic Jan 23 '13 at 08:13
  • If you need to only show the spinner on some of the events (and still be compatible with jQuery 1.8+, you should use ajaxSend and ajaxComplete: http://stackoverflow.com/a/15763341/117268 – Emil Stenström Apr 02 '13 at 11:50
  • @nickf can you update this showing jQuery >= 1.9 should only be attached to document ... couldn't see other comments until I came back but ended up debugging myself to figure it out ... thought it may help someone else if they see this and don't click show more comments ... – sMyles Oct 21 '13 at 23:09
  • On the jquery doc page, it says >= 1.8 should only attach to the document, not 1.9. http://api.jquery.com/ajaxStart/ "As of jQuery 1.8, the .ajaxStart() method should only be attached to document." – Robert Noack Nov 28 '13 at 21:18
  • jQuery hide() and show() functions set css display prpoerty to none and block. This affect the postioning of other elemnts in page. In efect it makes the page jump, flicker, and perhaps even completely breaks the layout. – f470071 Jun 01 '15 at 09:45
  • a loading icon suggests the user should wait... the whole point of ajax is so the user has to wait less... so yes just attaching it to the generic ajax start and stop events is too global in general – Andrew Jan 31 '17 at 06:43
  • clean & succinct answer, was able to make the concept work in within less than 3 minutes. Thankss. – salimsaid Apr 07 '18 at 16:45
  • Need from JQuery ... url: ... `duringWait:` function () {} and just solve this. Feels like I've tried 30 approaches for a spinner gif on click in a tooltip no joy – gseattle Apr 07 '21 at 01:38
223

For jQuery I use

jQuery.ajaxSetup({
  beforeSend: function() {
     $('#loader').show();
  },
  complete: function(){
     $('#loader').hide();
  },
  success: function() {}
});
meager
  • 209,754
  • 38
  • 307
  • 315
kr00lix
  • 3,154
  • 1
  • 15
  • 11
  • 13
    works for me, the html should have something like:
    – yigal Jan 04 '12 at 06:40
  • 7
    This one was cleanest for me. Instead of ajaxSetup, I placed beforeSend: and complete: inside the $ajax({... statement. – kenswdev Dec 17 '12 at 15:49
  • 5
    Don't know what it's worth, but jQuery mentions in it's documentation that using _.ajaxSetup()_ is not recommended. [link](http://api.jquery.com/jQuery.ajaxSetup/) – pec Sep 09 '13 at 00:45
  • Note here that `beforeSend` is called before every call, whereas `ajaxStart` is triggered only when the *first* ajax method is called. Likewise for `ajaxStop`, it is called when the *last* ajax call finishes. If you have more than one concurrent request, the snippet in this answer won't work properly. – nickf Nov 28 '13 at 22:57
  • Does not work for me if the ajax call times out (Firefox 28 for Mac). – Sergey Orshanskiy Apr 22 '14 at 02:58
  • ajaxSetup is like general setting for ajax reauests – Hos Mercury Dec 30 '16 at 12:37
55

You can just use the jQuery's .ajax function and use its option beforeSend and define some function in which you can show something like a loader div and on success option you can hide that loader div.

jQuery.ajax({
    type: "POST",
    url: 'YOU_URL_TO_WHICH_DATA_SEND',
    data:'YOUR_DATA_TO_SEND',
    beforeSend: function() {
        $("#loaderDiv").show();
    },
    success: function(data) {
        $("#loaderDiv").hide();
    }
});

You can have any Spinning Gif image. Here is a website that is a great AJAX Loader Generator according to your color scheme: http://ajaxload.info/

Ivar
  • 4,655
  • 12
  • 45
  • 50
Seeker
  • 1,811
  • 4
  • 28
  • 54
  • 19
    The `success` won't be called if there is an error, but "you will always receive a `complete` callback, even for synchronous requests," according to [jQuery Ajax Events](http://docs.jquery.com/Ajax_Events). – Lee Goddard Sep 04 '12 at 12:02
25

You can insert the animated image into the DOM right before the AJAX call, and do an inline function to remove it...

$("#myDiv").html('<img src="images/spinner.gif" alt="Wait" />');
$('#message').load('index.php?pg=ajaxFlashcard', null, function() {
  $("#myDiv").html('');
});

This will make sure your animation starts at the same frame on subsequent requests (if that matters). Note that old versions of IE might have difficulties with the animation.

Good luck!

Josh Stodola
  • 77,975
  • 43
  • 178
  • 222
21
$('#message').load('index.php?pg=ajaxFlashcard', null, showResponse);
showLoad();

function showResponse() {
    hideLoad();
    ...
}

http://docs.jquery.com/Ajax/load#urldatacallback

sth
  • 200,334
  • 49
  • 262
  • 354
Brent
  • 21,918
  • 10
  • 42
  • 49
  • You could have timing problems there, don't you? – Tim Büthe Nov 14 '11 at 14:11
  • 2
    @UltimateBrent I think you got it wrong. Why is showLoad() not a callback? JavaScript will load content asynchronously. showLoad() will work even before the content is loaded if i'm correct. – Jaseem Dec 01 '11 at 11:49
  • 2
    @Jaseem I use a similar method too and it actually relies on async calls. I would place showLoad somewhere before the ajax call but the whole point is to show a spinner, let JS start the call, and kill the spinner in a callback after the AJAX has completed. – Joel Peltonen Dec 12 '12 at 11:40
20

If you are using $.ajax() you can use somthing like this:

$.ajax({
  url: "destination url",
  success: sdialog,
  error: edialog,
  // shows the loader element before sending.
  beforeSend: function() {
    $("#imgSpinner1").show();
  },
  // hides the loader after completion of request, whether successfull or failor.             
  complete: function() {
    $("#imgSpinner1").hide();
  },
  type: 'POST',
  dataType: 'json'
});

Although the setting is named "beforeSend", as of jQuery 1.5 "beforeSend" will be called regardless of the request type. i.e. The .show() function will be called if type: 'GET'.

Always Helping
  • 13,486
  • 4
  • 8
  • 26
Amin Saqi
  • 17,037
  • 7
  • 43
  • 68
11

Use the loading plugin: http://plugins.jquery.com/project/loading

$.loading.onAjax({img:'loading.gif'});
alex
  • 438,662
  • 188
  • 837
  • 957
Nathan Bubna
  • 6,540
  • 2
  • 33
  • 36
  • 10
    One plugin for this simple task? That is not really a good solution is it? Who wants to load 100 scripts on a page? – Jaseem Dec 01 '11 at 11:50
  • 2
    Agreed. Compress and concatenate, if you want better performance. – Nathan Bubna Apr 02 '12 at 14:05
  • 9
    Disagreed: separate and modularise, reuse code, if you want to be able to maintain your work and have others do so too. What the chance of your user having such a poor connection their client can't load a plugin as well as your code? – Lee Goddard Sep 04 '12 at 11:57
  • @NathanBubna The link is dead – SpringLearner Apr 07 '16 at 09:46
  • "What the chance of your user having such a poor connection their client can't load a plugin as well as your code?" Pretty good I'd say. – andrew oxenburgh Mar 08 '18 at 20:27
9

I ended up with two changes to the original reply.

  1. As of jQuery 1.8, ajaxStart and ajaxStop should only be attached to document. This makes it harder to filter only a some of the ajax requests. Soo...
  2. Switching to ajaxSend and ajaxComplete makes it possible to interspect the current ajax request before showing the spinner.

This is the code after these changes:

$(document)
    .hide()  // hide it initially
    .ajaxSend(function(event, jqxhr, settings) {
        if (settings.url !== "ajax/request.php") return;
        $(".spinner").show();
    })
    .ajaxComplete(function(event, jqxhr, settings) {
        if (settings.url !== "ajax/request.php") return;
        $(".spinner").hide();
    })
Community
  • 1
  • 1
Emil Stenström
  • 10,369
  • 8
  • 45
  • 70
  • Nice workaround. `ajaxComplete` seems to fire prematurely when requests retry, so I've used a combination of `ajaxSend` and `ajaxStop` and it works great. – Mahn Nov 26 '14 at 22:46
  • @SchweizerSchoggi: There are other topics that answer that question, use the search! – Emil Stenström Sep 25 '15 at 14:51
9

I also want to contribute to this answer. I was looking for something similar in jQuery and this what I eventually ended up using.

I got my loading spinner from http://ajaxload.info/. My solution is based on this simple answer at http://christierney.com/2011/03/23/global-ajax-loading-spinners/.

Basically your HTML markup and CSS would look like this:

<style>
     #ajaxSpinnerImage {
          display: none;
     }
</style>

<div id="ajaxSpinnerContainer">
     <img src="~/Content/ajax-loader.gif" id="ajaxSpinnerImage" title="working..." />
</div>

And then you code for jQuery would look something like this:

<script>
     $(document).ready(function () {
          $(document)
          .ajaxStart(function () {
               $("#ajaxSpinnerImage").show();
          })
          .ajaxStop(function () {
               $("#ajaxSpinnerImage").hide();
          });

          var owmAPI = "http://api.openweathermap.org/data/2.5/weather?q=London,uk&APPID=YourAppID";
          $.getJSON(owmAPI)
          .done(function (data) {
               alert(data.coord.lon);
          })
          .fail(function () {
               alert('error');
          });
     });
</script>

It is as simple as that :)

Brendan Vogt
  • 23,956
  • 33
  • 133
  • 226
9

Variant: I have an icon with id="logo" at the top left of the main page; a spinner gif is then overlaid on top (with transparency) when ajax is working.

jQuery.ajaxSetup({
  beforeSend: function() {
     $('#logo').css('background', 'url(images/ajax-loader.gif) no-repeat')
  },
  complete: function(){
     $('#logo').css('background', 'none')
  },
  success: function() {}
});
Paul
  • 567
  • 6
  • 6
8

You can simply assign a loader image to the same tag on which you later will load content using an Ajax call:

$("#message").html('<span>Loading...</span>');

$('#message').load('index.php?pg=ajaxFlashcard');

You can also replace the span tag with an image tag.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Umesh kumar
  • 81
  • 1
  • 1
  • 1
    It will be hard to manage UI this way. We may need completely different styles for the "loading" text and final message. We can use the callback method mentioned above for handling this problem – Jaseem Dec 01 '11 at 11:52
6

As well as setting global defaults for ajax events, you can set behaviour for specific elements. Perhaps just changing their class would be enough?

$('#myForm').ajaxSend( function() {
    $(this).addClass('loading');
});
$('#myForm').ajaxComplete( function(){
    $(this).removeClass('loading');
});

Example CSS, to hide #myForm with a spinner:

.loading {
    display: block;
    background: url(spinner.gif) no-repeat center middle;
    width: 124px;
    height: 124px;
    margin: 0 auto;
}
/* Hide all the children of the 'loading' element */
.loading * {
    display: none;  
}
borrel
  • 890
  • 8
  • 17
Lee Goddard
  • 8,401
  • 3
  • 41
  • 52
4

Note that you must use asynchronous calls for spinners to work (at least that is what caused mine to not show until after the ajax call and then swiftly went away as the call had finished and removed the spinner).

$.ajax({
        url: requestUrl,
        data: data,
        dataType: 'JSON',
        processData: false,
        type: requestMethod,
        async: true,                         <<<<<<------ set async to true
        accepts: 'application/json',
        contentType: 'application/json',
        success: function (restResponse) {
            // something here
        },
        error: function (restResponse) {
            // something here                
        }
    });
Shane Rowatt
  • 1,570
  • 18
  • 39
4
$('#loading-image').html('<img src="/images/ajax-loader.gif"> Sending...');

        $.ajax({
            url:  uri,
            cache: false,
            success: function(){
                $('#loading-image').html('');           
            },

           error:   function(jqXHR, textStatus, errorThrown) {
            var text =  "Error has occured when submitting the job: "+jqXHR.status+ " Contact IT dept";
           $('#loading-image').html('<span style="color:red">'+text +'  </span>');

            }
        });
2

This is the best way for me:

jQuery:

$(document).ajaxStart(function() {
  $(".loading").show();
});

$(document).ajaxStop(function() {
  $(".loading").hide();
});

Coffee:

  $(document).ajaxStart ->
    $(".loading").show()

  $(document).ajaxStop ->
    $(".loading").hide()

Docs: ajaxStart, ajaxStop

Fred K
  • 11,133
  • 13
  • 70
  • 90
  • Right, and you can use it together with the plugin mentioned in [this answer](https://stackoverflow.com/a/41000442/1016343) if you want ... – Matt Jul 31 '18 at 09:13
2

JavaScript

$.listen('click', '#captcha', function() {
    $('#captcha-block').html('<div id="loading" style="width: 70px; height: 40px; display: inline-block;" />');
    $.get("/captcha/new", null, function(data) {
        $('#captcha-block').html(data);
    }); 
    return false;
});

CSS

#loading { background: url(/image/loading.gif) no-repeat center; }
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
2

This is a very simple and smart plugin for that specific purpose: https://github.com/hekigan/is-loading

andcl
  • 2,473
  • 3
  • 25
  • 49
  • Really nice! Especially if you take a look at the demos ... Disable DOM elements, In Tag, Full Overlay, Element Overlay ... it provides everything you need. And you can easily combine Full Overlay with [this answer](https://stackoverflow.com/a/19910389/1016343) to get it working instantly. – Matt Jul 31 '18 at 09:09
2

I've used the following with jQuery UI Dialog. (Maybe it works with other ajax callbacks?)

$('<div><img src="/i/loading.gif" id="loading" /></div>').load('/ajax.html').dialog({
    height: 300,
    width: 600,
    title: 'Wait for it...'
});

The contains an animated loading gif until its content is replaced when the ajax call completes.

Quinn Comendant
  • 5,880
  • 2
  • 27
  • 29
1

I think you are right. This method is too global...

However - it is a good default for when your AJAX call has no effect on the page itself. (background save for example). ( you can always switch it off for a certain ajax call by passing "global":false - see documentation at jquery

When the AJAX call is meant to refresh part of the page, I like my "loading" images to be specific to the refreshed section. I would like to see which part is refreshed.

Imagine how cool it would be if you could simply write something like :

$("#component_to_refresh").ajax( { ... } ); 

And this would show a "loading" on this section. Below is a function I wrote that handles "loading" display as well but it is specific to the area you are refreshing in ajax.

First, let me show you how to use it

<!-- assume you have this HTML and you would like to refresh 
      it / load the content with ajax -->

<span id="email" name="name" class="ajax-loading">
</span>

<!-- then you have the following javascript --> 

$(document).ready(function(){
     $("#email").ajax({'url':"/my/url", load:true, global:false});
 })

And this is the function - a basic start that you can enhance as you wish. it is very flexible.

jQuery.fn.ajax = function(options)
{
    var $this = $(this);
    debugger;
    function invokeFunc(func, arguments)
    {
        if ( typeof(func) == "function")
        {
            func( arguments ) ;
        }
    }

    function _think( obj, think )
    {
        if ( think )
        {
            obj.html('<div class="loading" style="background: url(/public/images/loading_1.gif) no-repeat; display:inline-block; width:70px; height:30px; padding-left:25px;"> Loading ... </div>');
        }
        else
        {
            obj.find(".loading").hide();
        }
    }

    function makeMeThink( think )
    {
        if ( $this.is(".ajax-loading") )
        {
            _think($this,think);
        }
        else
        {
            _think($this, think);
        }
    }

    options = $.extend({}, options); // make options not null - ridiculous, but still.
    // read more about ajax events
    var newoptions = $.extend({
        beforeSend: function()
        {
            invokeFunc(options.beforeSend, null);
            makeMeThink(true);
        },

        complete: function()
        {
            invokeFunc(options.complete);
            makeMeThink(false);
        },
        success:function(result)
        {
            invokeFunc(options.success);
            if ( options.load )
            {
                $this.html(result);
            }
        }

    }, options);

    $.ajax(newoptions);
};
guy mograbi
  • 22,955
  • 13
  • 75
  • 115
1

If you don't want to write your own code, there are also a lot of plugins that do just that:

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
keithhackbarth
  • 6,681
  • 3
  • 22
  • 31
1

If you plan to use a loader everytime you make a server request, you can use the following pattern.

 jTarget.ajaxloader(); // (re)start the loader
 $.post('/libs/jajaxloader/demo/service/service.php', function (content) {
     jTarget.append(content); // or do something with the content
 })
 .always(function () {
     jTarget.ajaxloader("stop");
 });

This code in particular uses the jajaxloader plugin (which I just created)

https://github.com/lingtalfi/JAjaxLoader/

ling
  • 7,505
  • 3
  • 41
  • 39
1

My ajax code looks like this, in effect, I have just commented out async: false line and the spinner shows up.

$.ajax({
        url: "@Url.Action("MyJsonAction", "Home")",
        type: "POST",
        dataType: "json",
        data: {parameter:variable},
        //async: false, 

        error: function () {
        },

        success: function (data) {
          if (Object.keys(data).length > 0) {
          //use data 
          }
          $('#ajaxspinner').hide();
        }
      });

I am showing the spinner within a function before the ajax code:

$("#MyDropDownID").change(function () {
        $('#ajaxspinner').show();

For Html, I have used a font awesome class:

<i id="ajaxspinner" class="fas fa-spinner fa-spin fa-3x fa-fw" style="display:none"></i>

Hope it helps someone.

Jaggan_j
  • 428
  • 1
  • 7
  • 8
1

I do this:

var preloaderdiv = '<div class="thumbs_preloader">Loading...</div>';
           $('#detail_thumbnails').html(preloaderdiv);
             $.ajax({
                        async:true,
                        url:'./Ajaxification/getRandomUser?top='+ $(sender).css('top') +'&lef='+ $(sender).css('left'),
                        success:function(data){
                            $('#detail_thumbnails').html(data);
                        }
             });
0

You can always use Block UI jQuery plugin which does everything for you, and it even blocks the page of any input while the ajax is loading. In case that the plugin seems to not been working, you can read about the right way to use it in this answer. Check it out.

SamyCode
  • 608
  • 2
  • 8
  • 26