How to make JS wait until DOM is updated?

How to make JS wait until DOM is updated?
0

#1

I have one of these “I can make the problem go away, but I want to know how I would have fixed it” questions. The question is, basically, what’s the simplest and most straightforward way to make a line of JS wait until updates to the HTML, set in motion by a preceding line, have rendered?

I’m making a simple hangman game (here’s the whole repo, if you’re curious). Right now when the user wins or loses a game, an alert is triggered to inform them of the fact. But the command to either display their final, winning letter, or to draw the last leg of the hangman, hasn’t taken effect on the screen before flow is blocked by the alert. Here are the winning or losing functions:

const wrongGuess = letter => {
	totalWrong++;
	document.getElementById("wrongGuesses").innerHTML += " " + letter; //bootcamp assigment; we're not supposed to have learned jQuery yet
	document.getElementById("svg").innerHTML += svgElements[totalWrong - 1];
	alreadyGuessed.push(letter);
	if (totalWrong === 6) {
		alert("You lose. The word was: " + mysteryWord);
		reset();
	}
}

const rightGuess = letter => {
	for (var i = 0; i < mysteryWord.length; i++) {
		if (mysteryWord[i] === letter) {
			document.getElementById("blank" + i).innerHTML = " " + letter;
			totalRight++; //iterate here to account for multiple ocurrences of the same letter
		}
	}
	alreadyGuessed.push(letter);
	if (totalRight === mysteryWord.length) {
		alert("Yay!! You win!");
		reset();
	}
}

Now, I could certainly fix the issue by not using an alert at all, because they’re annoying and stupid and flow-blocking; I could display the message directly on the page. But I want to know, for the future, what is the best method of making one block of code wait while an earlier block updates HTML? I could imagine:

  • a plain old callback (does this require the “success” handler?)
  • promises and “then” (but clearly I don’t understand them yet. What returns what now?)
  • what about this suggestion of requestAnimationFrame?
  • attach a jQuery event handler to the element in question–$.ready? or some such? Is there a non-jQuery way to simply say “listen for when this ID is updated”–not “loaded (for the first time)” but updated?

#2

What about something like setTimeout? You could have:

var alertDelay = 2000; // 2000 milliseconds or 2 seconds
if (totalRight === mysteryWord.length) {
  setTimeout( function() { 
    alert("Yay!! You win!");
    reset();
  }, alertDelay);
}

#3

Several years ago there was an answer on Stackoverflow that said you could use setTimeout with a timeout of 0. That seemed to work in all the major browsers then, but it no longer works in Firefox 62. Now setTimeout only works if the timeout is long enough, where “long enough” is likely to vary from system to system and platform to platform.

I recently did some experimenting to try alternatives. What seems to work in all the major browsers that I can test (Firefox, Chrome, Safari, Edge, and mobile Safari) is double requestAnimationFrame.

function call_after_DOM_updated(fn)
{
    intermediate = function () {window.requestAnimationFrame(fn)}
    window.requestAnimationFrame(intermediate)
}

The following all fail in at least one browser from the list above: setTimeout(…,0), double setTimeout(…,0), requestAnimationFrame, requestIdleCallback, double requestIdleCallback.

Here’s a quick and dirty test self-contained HTML/JavaScript file you can run locally to try out all those methods: https://pastebin.com/n26iVs7d


#5

If there’s an animation going on somewhere else with your stick figure or whatever, that could be the main cause of your problem.