Most of the time, JavaScript and the browser DOM act exactly as you would expect. There are no surprises. Sometimes, though, when you are surprised, it is painful.

Under this paragraph, there is a block of text that you cannot see. Why can’t you see it? Because the CSS has an opacity: 0 setting.

Doing very important stuff...

Here, we have a button.

-----

What should happen when we click this button?

  1. Show the block.
  2. Do some computational work.
  3. Hide the block.

Try it and see what happens. Here’s the code for this button.

$("#clicker1").on("click", function () {
    $("#block1").css("opacity", 1);
    let maxDuration = 2000;
    let startTime = Date.now();
    let endTime = Date.now();
    let array = [];
    while ((!endTime) || (endTime - startTime < maxDuration)) {
        let x = Math.random() * 100;
        let s = x.toFixed(3);
        array.push(s);
        endTime = Date.now();
    }
    let duration = "Done in " + ((endTime - startTime) / 1000).toFixed(3) + " seconds";       
    $("#duration1").text(duration);
    $("#block1").css("opacity", 0);
});

Hmmm… None of that worked as expected. Why? Remember: JavaScript is single-threaded. We never gave the thread time to update the screen.

Let’s try again, this time using the setTimeout function to give the call stack time to update the browser.

Doing very important stuff...

-----

What did we do differently?

$("#clicker2").on("click", function () {
    $("#block2").css("opacity", 1);
    setTimeout(function () {
        let maxDuration = 2000;
        let startTime = Date.now();
        let endTime = null;
        let array = [];
        while ((!endTime) || (endTime - startTime < maxDuration)) {
            let x = Math.random() * 100;
            let s = x.toFixed(3);
            array.push(s);
            endTime = Date.now();
        }
        let duration = "Done in " + ((endTime - startTime) / 1000).toFixed(3) + " seconds";       
        $("#duration2").text(duration);
        $("#block2").css("opacity", 0);
    }, 0);
});

So why does this work? The setTimeout function adds the next round of operations on the end of the call stack. This gives the jQuery DOM operation time to run, meaning: the UI has time to update. Unfortunately, this is just one of those annoying little things that can happen when working with the browser, and it’s a handy solution to keep around.