DOM Manipulation not executing instantly

64 views Asked by At

I've experienced the issue that DOM Manipulations only take effect when the browser ran through all iterations, i.e. while, for, -loops.

Example:

var text = document.getElementById("text");
for (var i = 0; i < 100000000; i++) {
  if (i % 1000000 == 0) {
    setTimeout(function() {
      text.innerHTML += "|";
    }, 0);
  }
}
<p id="text"></p>

I'd like to see a progressbar-ish behavior, instead the DOM is manipulated only when he ran through the for-loop.

I tried an asynchronous approach with setTimeout with no success.

Is there a good way to achieve this?

3

There are 3 answers

0
InsOp On BEST ANSWER

Since Javascript runs in a single thread and therefore everything is blocked until it is finished processing the calculations.

In order to have a non-blocking calculation, you can use webworkers and update the dom when the worker dispatches the postMessage event.

Example:

Main code:

var text = document.getElementById("text");
var myWorker = new Worker("js/forloop.js");
myWorker.postMessage(2000);
myWorker.onmessage = function(e) {
   //e.data is an object which is passed from the worker
   if (e.data === true)
      text.innerHTML += "|";
}

WebWorker code:

onmessage = function(e) {
   for (var i = 0; i < e.data; i++) {
     postMessage(true);
   }
}

Further reading: Webworkers

0
Rick Hitchcock On

Change the timeout from 0 to 500, and you will see it progress:

var text = document.getElementById("text");
for (var i = 0; i < 100000000; i++) {
  if (i % 1000000 == 0) {
    setTimeout(function() {
      text.innerHTML += "|";
    }, 500);
  }
}
<p id="text"></p>

However, the program has to finish counting to 100 million before it goes into an idle state to handle the first timer.


Here's an alternative method, which won't take up so many computer resources:

var text = document.getElementById("text");
for (var i = 0; i < 100; i++) {
  setTimeout(function() {
    text.innerHTML += "|";
  }, 10*i);
}
<p id="text"></p>

4
TW- On

Instead of using loops and setTimeout, use the similar Javascript tool setInterval. This will run a function repeatedly every N milliseconds. You can use a counter variable to keep track of when to add progress bars and when to stop! http://jsfiddle.net/4odd386e/

var text = document.getElementById("text");
var i = 0;
function addOne() {
    i += 1;
    if (i % 10 === 0) {
        text.innerHTML += "|";
    }
    if (i === 1000) {
        // Progress complete
        clearInterval(initProgress);
    }
}
var initProgress = setInterval(addOne, 0);

Also, the high numbers you initially used were causing very slow progress, so I used 10 and 1000 as examples. This code will work with higher numbers, but it will take a long time to show results.