javascriptjquerydomhigh-load

jQuery/Javascript - How to wait for manipulated DOM to update before proceeding with function


What I'm trying to do is to update a simple div to say "Processing..." before executing a CPU-intensive script (it takes 3-12 seconds to run, no AJAX) then update the div to say "Finished!" when done.

What I'm seeing is the div never gets updated with "Processing...". If I set a breakpoint immediately after that command, then the div text does get updated, so I know the syntax is correct. Same behavior in IE9, FF6, Chrome13.

Even when bypassing jQuery and using basic raw Javascript, I see the same issue.

You'd think this would have an easy answer. However, since the jQuery .html() and .text() don't have a callback hook, that's not an option. It's also not animated, so there is no .queue to manipulate.

You can test this yourselves using the sample code I prepared below that shows both the jQuery and Javascript implementations with a 5 second high-CPU function. The code is easy to follow. When you click either the button or the link, you never see "Processing..."

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script type="text/javascript">
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    do {start = new Date();} while (end-start > 0);
    document.getElementById('msg').innerHTML = 'Finished JS';   
}
$(function() {
    $('button').click(function(){
        $('div').text('Processing JQ...');  
        start = new Date();
        end = addSecs(start,5);
        do {start = new Date();} while (end-start > 0);
        $('div').text('Finished JQ');   
    });
});
</script>
</head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>

Solution

  • set it to processing, then do a setTimeout to prevent the cpu intensive task from running until after the div has been updated.

    <!DOCTYPE html>
    <html>
    <head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
    <script>
    function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
    function doRun() {
        document.getElementById('msg').innerHTML = 'Processing JS...';
        setTimeout(function(){
             start = new Date();
             end = addSecs(start,5);
             do {start = new Date();} while (end-start > 0);
             document.getElementById('msg').innerHTML = 'Finished Processing';   
        },10);
    }
    $(function() {
        $('button').click(doRun);
    });    
    </script>
        </head>
    <body>
        <div id="msg">Not Started</div>
        <button>jQuery</button>
        <a href="#" onclick="doRun()">javascript</a>
    </body>
    </html>
    

    you can modify the setTimeout delay as needed, it may need to be larger for slower machines/browsers.

    Edit:

    You could also use an alert or a confirm dialog to allow the page time to update.

    document.getElementById('msg').innerHTML = 'Processing JS...';
    if ( confirm( "This task may take several seconds. Do you wish to continue?" ) ) {
         // run code here
    }