1

I'm trying to get a wait gif to display while my datatables are loading. I'm simply calling $(element).show() just prior to entering my $(element).each() loop that displays all child grids (ie. DisplayInnerTable()) using datatables.

It seems I've narrowed the problem down to the .each() method. If I remove all the code inside the .each() the wait gif still won't display, and yes, i remove the .hide() also. If I put a break point just before the .each() the wait gif displays. Seems to be a timing issue, like the .each() is happening so fast the wait.gif inside the div does not have time to render. And while on that note, why would Javascript thread prevent html element manipulation?

I did find some similar articles, but none of them answer this specific issue. Most of the other articles recommend using setTimeout(). well I tried that in every way possible, no luck. Any help, hints or clues will be much appreciated.

Here's my code snippets:

  $(document).ready(function () 
  {
    $('#expand-all').on('click', function () {
        $("#loader-wait-ani").show();
        ExpandAllChildGrids();
    });

    function ExpandAllChildGrids()
    {
       var tr;
       $('#result-table tbody tr').each(function (value, index) {
          tr = $(this).closest('tr');
          var row = outerTable.row(tr);
          userId = row.cell(tr, 1).data();

        DisplayInnerTable(userId, row, tr);
      });
      $('#expand-all-img').attr('src', '/Images/minus.gif');
      $("#loader-wait-ani").hide();
   }
 });

Here's my html:

    <div style="width:100%; margin: 0 auto; text-align:center;">
        <div id="loader-wait-ani" class="modal" style="display: none; z-index: 300;">
            <img src="~/Images/waitbar.gif" alt="Pleae Wait..." width="250" height="20" />
        </div>
    </div>
t.niese
  • 39,256
  • 9
  • 74
  • 101
Robert
  • 101
  • 1
  • 10
  • Do you have multiple id="loader-wait-ani" ? – RIanGillis Aug 05 '16 at 16:47
  • Have you tried using a callback function on `.show()`? – Steven B. Aug 05 '16 at 16:48
  • You show and hide the loading gif in the same code execution routine. There will not rendering take place in between. Thats why it won't show up. What does the `DisplayInnerTable` function do internally? Is there some async code within the `DisplayInnerTable` that populates the cells? – t.niese Aug 05 '16 at 16:48
  • @RlanGillis - no, i don't have multiple id="loader-wait-ani" divs – Robert Aug 05 '16 at 17:58
  • @lamelemon - I have not thought of using a callback on .show() - I'll look into how I could implement that. – Robert Aug 05 '16 at 17:58
  • @t.niese - The DisplayInnerTable() does hit the back end, but with async: false - yes, it builds the child tables and populates the cells. – Robert Aug 05 '16 at 17:59
  • @Robert you might also need to add a callback function to `DisplayInnerTable()` so the image is not hidden as soon as `ExpandAllChildGrids()` fires. – Steven B. Aug 05 '16 at 18:17

2 Answers2

1

Try this. It's a callback on show() and a callback on DisplayInnerTable()

 $(document).ready(function () 
  {
    $('#expand-all').on('click', function () {
        $("#loader-wait-ani").show("fast", function(){
            ExpandAllChildGrids();
        });
    });

    function ExpandAllChildGrids()
    {
       var tr;
       callDisplayInner(hideLoader);
      $('#expand-all-img').attr('src', '/Images/minus.gif');

   }
 });

function hideLoader(){
      $("#loader-wait-ani").hide();
}

function callDisplayInner(hideLoader){
      $('#result-table tbody tr').each(function (value, index) {
          tr = $(this).closest('tr');
          var row = outerTable.row(tr);
          userId = row.cell(tr, 1).data();

        DisplayInnerTable(userId, row, tr);
      });
      hideLoader();
}
Steven B.
  • 8,962
  • 3
  • 24
  • 45
  • That would hide the loader gif as soon as the first request has finished. So this is not the best solution, if one request takes much more time then the others. – t.niese Aug 05 '16 at 18:24
  • @t.niese Yeah, I had it on the wrong function. updated to what I think will work? – Steven B. Aug 05 '16 at 18:25
  • 1
    This probably might work, if the requests are not async, but you should assume that the browser window completely freezes, halting every thing including the gif animation. Anyway if the request time requires a loading indicator, then a sync request is a really bad idea. Beside that `async: false` is deprecated since jQuery 1.8 and should not be used. – t.niese Aug 05 '16 at 18:37
  • @t.niese Unrelated to my actual answer, if he's using ajax, wouldn't this be a good place to just use the `.ajaxStart()` and `.ajaxStop()` handlers? – Steven B. Aug 05 '16 at 18:48
  • @lamelemon - your solution above immediately solved my problem of the wait gif not showing, it shows now. However the animation is suspended, as ( t.niese ) mentioned above, the browser freezes every thing. I'm getting a bad feeling about the choice of using Datatables library. If you know of anything I may be missing that will allow the animation to not freeze would be extremely helpful.. Thank you both for your prompt and thorough responses!! – Robert Aug 08 '16 at 18:03
1

Using async: false in the each loop you will block the browser until the reponses for all requests have been sended. The browser would not do any rendering until your code reaches the end of your on click callback.

As of that you would need to request the data async and after all data is loaded you have to hide your loading gif. I assume you use the ajax method of jQuery so you could use the returned Defere to keep track of your requests.

Some thing like that:

function DisplayInnerTable(userId, row, tr, callback) {
  //return the Defered object of the jQuery request
  return $.ajax('/request/to/url');
}


$(document).ready(function() {
  $('#expand-all').on('click', function() {
    $("#loader-wait-ani").show();
    ExpandAllChildGrids();
  });


  function ExpandAllChildGrids() {
    var tr;
    var allRequests = [];
    $('#result-table tbody tr').each(function(value, index) {
      tr = $(this).closest('tr');
      var row = outerTable.row(tr);
      userId = row.cell(tr, 1).data();

      //collect all Deferes in an array
      allRequests.push(DisplayInnerTable(userId, row, tr));
    });


    $.when.apply($, allRequests)
      .then(function() {
        // will be executed when all requests are finished
        $('#expand-all-img').attr('src', '/Images/minus.gif');
        $("#loader-wait-ani").hide();
      })
  }
});

Here some related informations:

Community
  • 1
  • 1
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • Your solution certainly seems like the right way to go, but unfortunately using Datatables the way I have it designed the grids will not render if I have async: true - otherwise your solution works. I set async: true and sure enough the wait gif displayed, but also as expected the grids will not render. – Robert Aug 08 '16 at 17:54