Tony R
Tony R

Reputation: 11524

DOM update followed by a big loop doesn't render in time

A piece of my page has ~9000 elements in it and it has to be rebuilt often, which can take a few seconds.

So, I made a little overlay widget that covers the element with a Loading... message. Right before I rebuild the element, I call showOverlay(), and after the loop I call hideOverlay().

But the loop locks up the page before my Loading... message is displayed, and so it never appears.

function rebuild() {
  showOverlay();    // The overlay never appears...
  for (var i=0;i<9000;i++) {
    // append element...
  }
  hideOverlay();
}

How can I wait for the overlay to be rendered BEFORE I start the loop?

Upvotes: 4

Views: 1665

Answers (4)

Kevin
Kevin

Reputation: 1157

you could try this:

var loopCount = 0,
    build = function() {

    var frgm = document.createDocumentFragment();
    for (var i = 0; i < 200 && loopCount < 9000; i++) {
        // some codes
        frgm.appendChild(someElement);
        loopCount ++;
    }
    parentNode.appendChild(frgm);

    if (loopCount < 9000) {
         setTimeout(build, 10);
    }
};

function rebuild() {
  showOverlay();    // The overlay never appears...
  build();
  hideOverlay();
}

Upvotes: 0

Tony R
Tony R

Reputation: 11524

The other answers show how to do this in a "pseudo-threading" way, using timers.

In the meantime I have learned about Web Workers, which are a threading solution that separates script execution from DOM updates.

They are currently supported in WebKit and Firefox, and support is planned for IE 10.

Upvotes: 0

James Hay
James Hay

Reputation: 7325

You need to put your loop inside a set timeout so that it doesn't hold up the page. Even if your overlay is displayed, nobody likes their page freezing

var counter = 0;
function rebuild() {
    showOverlay();
    doWork();
}
function doWork() {
    if(counter < 9000){
        // append element
        counter++;
        setTimeout(function(){
            doWork();
        },10);
    }
    else {
        hideOverlay();
    }
}

EDIT: This answer will actually take significantly longer to process the page though. Somewhere in the realm of 90 seconds which is pretty unacceptable, the other alternative could be to set a timeout every 100 iterations, which will add about 1 sec to the total load time, but should stop the page from freezing.

function doWork() {
    if(counter < 9000){
        // append element
        if(counter % 100 == 0) {
            setTimeout(function(){
                doWork();
            },10);
            counter++;
        }
        else {
            doWork();
            counter++;
        }
    }
    else {
        hideOverlay();
    }
}

Upvotes: 1

Eugen Rieck
Eugen Rieck

Reputation: 65314

function do_rebuild() {
  for (var i=0;i<9000;i++) {
    // append element...
  }
  hideOverlay();
}


function rebuild() {
  showOverlay();    // The overlay will appear
  window.setTimeout('do_rebuild();',1);
}

Is the only cross-browser way I know of.

Upvotes: 4

Related Questions