InsOp
InsOp

Reputation: 2699

DOM Manipulation not executing instantly

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?

Upvotes: 0

Views: 70

Answers (3)

InsOp
InsOp

Reputation: 2699

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

Upvotes: 0

TW-
TW-

Reputation: 33

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.

Upvotes: 1

Rick Hitchcock
Rick Hitchcock

Reputation: 35670

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>

Upvotes: 0

Related Questions