AJAX loading spinner without flickering artifacts

We were embedding a spinner to give user feedback while loading data from a server which might take a little longer (but can also be pretty quick in most cases).

Implementing the spinner itself isn’t that hard, but we found that quick responses from the server caused visual artifacts flickering up because the spinner was only visible for a few milliseconds (probably roughly 30ms).

The solution we chose to implement looks like this:

  • before triggering the AJAX request, schedule the code that loads the spinner 150ms into the future
  • if the AJAX request returns and the spinner did not materialize yet, cancel the scheduled code
  • if the AJAX request returns and the spinner was materialized, schedule the code to hide the spinner 200ms into the future

We also had two smaller variations:

  • if the replacement looks almost identical to what was shown before, don’t hide it until the spinner actually shows up.
  • if the replacement looks usually a lot different, then blank the area immediately waiting for the spinner to show up or not.

We implemented this with MochiKit and here is some sample code:

var expansion_element = $('mydivcontainingstuff');
var spinner_loader = MochiKit.Async.callLater(0.15, function() {
   MochiKit.DOM.addElementClass(expansion_element, 'loading');
   MochiKit.DOM.addElementClass(expansion_element, 'loading-spinner');
   spinner_loader = null;
});
d = MochiKit.Async.doSimpleXMLHttpRequest(ajax_url);
d.addCallback(function(result){
   $('mystuffdivcontainingstuff').innerHTML = result.responseText;

   if (spinner_loader != null ) {
       spinner_loader.cancel();
   } else {
       MochiKit.Async.callLater(0.2, function() {
       MochiKit.DOM.removeElementClass(expansion_element, 'loading-spinner');
       MochiKit.DOM.removeElementClass(expansion_element, 'loading');
   });
}

And here’s the CSS:

.loading-spinner {
  width: 100px;
  height: 100px;
  margin-left: auto !important;
  margin-right: auto !important;
  background-image: url(ajax-loader.gif);
  background-repeat: no-repeat;
  background-position: 50% 50%;
} 

.loading * {
  display: none;
}

Leave a Reply

%d bloggers like this: