2

I have a button. onClick the button fires a JS function. In this function on the very first line of code I change the button's HTML using jQuery. Then the function takes a couple seconds. The button HTML only changes (in browser) after the complete function is finished.

https://jsfiddle.net/wqps1r0k/

The code:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<button class="btn btn-primary" onclick="setToDisabled(this)">Toggle</button>

    <script type="text/javascript">
    function setToDisabled(btn) {
      //log
      console.log('start');

      //how to force the browser to show this text immediately?
      $(btn).html("TEXT CHANGED ON START OF FUNCTION");

      //create a 2 seconds delay
      var d1 = new Date();
      var t1 = d1.getTime();
      var keeprunning = true;
      while(keeprunning) {
        var d2 = new Date(); 
        var t2 = d2.getTime();
        var dif = t2 - t1;
        if((dif / 1000) > 2) keeprunning = false;
      }
    
      //log
      console.log('done');
    }
    </script>

In this example, would it be possible to change the button text at the beginning of the flow and see this in my browser immediately? For example to change the text to 'Loading...'.

If I inspect the button you can see the text changing directly, but it is only visible after the function completes.

Ps. I use chrome

Ayo K
  • 1,719
  • 2
  • 22
  • 34
Marcel
  • 105
  • 6
  • 2
    Don't block the UI thread. Instead, use `setTimeout` – SLaks Jan 31 '18 at 15:42
  • Possible duplicate of [jQuery/Javascript - How to wait for manipulated DOM to update before proceeding with function](https://stackoverflow.com/questions/7342084/jquery-javascript-how-to-wait-for-manipulated-dom-to-update-before-proceeding) – Heretic Monkey Jan 31 '18 at 15:48

1 Answers1

6

The issue is because the while() loop is synchronous and starts executing before the browser thread which updates the UI has a chance to execute. Therefore the UI is blocked from changing until your loop finishes, at which point the new HTML is applied to the button.

To fix this, use a timeout for delaying some logic. It's asynchronous and does not interfere with the UI:

$('.btn').click(function() {
  console.log('start');
  $(this).html("TEXT CHANGED ON START OF FUNCTION");

  setTimeout(function() {
    console.log('done');
  }, 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btn btn-primary">Toggle</button>

Also note that I removed the outdated on* event attribute with an unobtrusive event handler as this is considered much better practice.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Thanks a lot, makes sense. However, in my case the delay is caused by a synchronous AJAX request (I know, bad practice and even deprecated), is it in any way possible to change the button UI before this synchronous request? – Marcel Jan 31 '18 at 15:52
  • @MarcelVuijk The same way, just do your ajax call in the `setTimeout`. (You can set the timeout delay to `0`.) – Ivar Jan 31 '18 at 15:54
  • Or better yet, don't use synchronous AJAX requests. There's really no need to when you structure your code to use callbacks. Nevertheless, glad this solved your problem. – Rory McCrossan Jan 31 '18 at 16:00
  • Yeah I know. But I use a plugin (JQPlot) and it requires synchronous AJAX request for the AJAX dataRenderer so I was kind of forced to use it. But it's all working now, thanks again. – Marcel Jan 31 '18 at 19:48
  • In that case I'd find another chart rendering plugin. Forcing you to use `async: false` is enough reason to not use that library – Rory McCrossan Jan 31 '18 at 20:47