1

I have a web page where the user can select some options from a menu. Each option takes a few seconds in order to be downloaded, processed and showed. So during this time I try to show a loader gif. But I'm experiencing some weird problems and I'm stucked.

I have created a very simple jsfiddle, erasing everything except the minimum code to reproduce the problem:

$('#ActnBtn').click(function() {
  document.getElementById("loading").style.display = "block";
  console.log("loader should be showed");

  //Intensive code simulation, probably you should adjust the "times" value to the power of your device
  var times = 5000000000;
  for (let i = 0; i < times; i++) {
    var j = (i ^ 2 + 1) / (i - 1) ^ 2;
  }
  console.log("finished")

  setTimeout(function() {
    document.getElementById("loading").style.display = "none";
  }, 1000); 
  // 1sec timeout to be able to see the loader
  // document.getElementById("loading").style.display = "none" 
  // Without timeout loader isn't showed because "show" and "hide" happens at the same time
});
#loading {
  display: none;
  position: fixed;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  z-index: 9999;
  background: url(https://media.giphy.com/media/lrnlLO9o53qH44g7kC/giphy.gif) center no-repeat #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id=loading></div>

<button id="ActnBtn" type="button">Do some intesive work</button>

Long history short, the loader is showing after the "intesive code". For me this makes absolutely no sense and I don't know how to continue now. Any help is appreciated. Thank you so much.

Regards,

Héctor

PD: console shows messages like: [Violation] 'click' handler took 4370ms

adiga
  • 34,372
  • 9
  • 61
  • 83
Héctor
  • 399
  • 3
  • 16
  • 1
    Not so weird at all. Your `style.display = "block"` is in the same execution as your calculations. You never give it a chance to update the UI. But you mention something about downloading, which is usually asynchronous, so that problem should not appear there. – Ivar Sep 24 '19 at 15:57
  • And how I give a chance to update the UI? In my real application, loader shows just after the data download is complete and just before the processing starts. Why doesn't the loader show while waiting for the server response? – Héctor Sep 24 '19 at 16:04
  • @Héctor https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame – GammaGames Sep 24 '19 at 16:05
  • You can use a `setTimeout()` around the calculations so that it will be processed in the next event loop cycle ([example](https://jsfiddle.net/0k5ztvo6/)). But that still will freeze the rest of your browser. You could look in to [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers). Related: [How to keep animated gif running while doing intense calculations](https://stackoverflow.com/questions/7300632/how-to-keep-animated-gif-running-while-doing-intense-calculations) – Ivar Sep 24 '19 at 16:08
  • Thank you everyone. I thought that interrupted the UI to make changes happens before jump to the following statement. Is working now, thanks again :) – Héctor Sep 24 '19 at 16:23
  • [How do I create a runnable stack snippet?](https://meta.stackoverflow.com/questions/358992) – adiga Sep 24 '19 at 16:31

1 Answers1

0

This part of javascript runs synchronous.

You need to put your intensive code also in a setTimeout.

This is because the changes to the DOM will take place after the javascript returns and finished.

Please find here the corrected example (with a ninja, because I didn't get your gif running), I put your iteration in a function for the timer etc.

$('#ActnBtn').click(function() {
    document.getElementById("loading").style.display = "block";
          console.log("loader should be showed");
    setTimeout(function(){
    

    //Intensive code simulation, probably you should adjust the "times" value to the power of your device
      var times = 5000000000;
      for(let i=0; i<times; i++){
          var j = (i^2+1)/(i-1)^2;
      }
      console.log("finished")

      setTimeout(function(){document.getElementById("loading").style.display = "none";},1000); //1sec 
   },0);
});
#loading {
    display: none;
        position: fixed;
        left: 0px;
        top: 0px;
        width: 100%;
        height: 100%;
        z-index: 9999;
        background: url('https://loading.io//assets/img/pricing/ninja.gif') center no-repeat #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id=loading></div>

<button id="ActnBtn" type="button">Do some intesive work</button>

I hope this helps you and gives enough explanation.