开发者

Browser not updating text before inserting Ajax response with innerHTML

I'm working on a CMS which has a tree like page structure, so I am trying to emulate the Windows Explorer one uses to browser their C drive for example. So initially I list the pages at the root level, and using an onClick event and AJAX, clicking on a root page will display pages below that, in a DIV I've created/allocated for that.

All works fine, and I have an animated loading gif displayed in another DIV while xmlhttp.send is running, which is switched off when if (xmlhttp.readyState==4 && xmlhttp.status==200) is true.

The problem is that when there are a large number of sub pages (1,000 or so and yes, the client created them), the AJAX completes and the it gets to document.getElementById(DivId).innerHTML = xmlhttp.responseText; which causes the gif to stop spinning. So I've googled that and it seems this is a browser issue.

So I thought, I'll use the gif during the AJAX call and display a wait text while the browser is rendering the new innerHTML. However, despite it taking several seconds, this text never gets displayed, I just see the frozen gif and then once rendered, the "done" text.

If I comment out the "done" line, the wait text does get displayed though.

The code is below:

function getPages(page_id, DivId)
{
    var loadingicon_div = "page_" + page_id + "_loadingicon";
    var loading_icon = 'image here, not allowed to post images..'; 

    document.getElementById(loadingicon_div).innerHTML = loading_icon;

    xmlhttp = new GetXmlHttpObject();
    xmlhttp.onreadystatechange = function() 
                    { 
                        if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
                        {
                            document.getElementById(loadingicon_div).innerHTML = "Retrieved pages from the server, please wait while your browser displays them ...";
                            document.getElementById(Di开发者_运维技巧vId).innerHTML = xmlhttp.responseText;
                            document.getElementById(DivId).style.padding="5px";
                            document.getElementById(loadingicon_div).innerHTML = "done";
                        }
                    }

    var url="tree_ajax.php";

    xmlhttp.open("GET",url,true);
    xmlhttp.send(null);
}


You should really be using a framework. I recently whipped up almost the same thing using ajax treeview built into extjs. Trust me this will save you lots of headaches. Cross browser dom is a PITA.

I know this doesn't answer your specific question, but heed my advice!

http://dev.sencha.com/deploy/dev/examples/tree/reorder.html


The only solution to this is a redesign. - no it's not, but you really should - read on ;)

The reason the animated gif stops animating is because the browser is using all it's muscle to append new elements to the DOM and to re-render/re-paint the page after this is done. There simply is no cputime left for it to animate the gif.

The slower the computer/browser is the more you will see this problem, but if you build a page with enough elements, you can make this happen in google chrome on a 4ghz computer.

The browser is a singlethreaded beast by nature when it comes to javascript/DOM manuipulation/rendering/painting and thus it can only really do one thing at a time.

It tries to make up for this by breaking complex operation up into slices and then run these slices in a way that makes it seem like it can do multiple things at once (much like operating systems does on single core machines, although much simpler)

But once you get a sufficiently complex dom structure the re-rendering/re-painting slice becomes so big that the browser seems to freeze while it happens.

As for the text not appearing:

If you wish to make a small DOM manipulation take effect (like inserting your text) before a you do a large DOM-manipulation, you need to make the browser treat this as two separate slices (which it doesn't want to do, because DOM-manipulation and subsequent re-rendering/re-painting is very cpu-costly)

To do this, break the callstack in javascript by using a setTimeout.

This will allow the browser to do a re-render/re-paint before the next DOM manipulation and subsequent re-render/re-paint.

Usually it is enough to do the setTimeout with a zero based delay, since the setTimeout in itself breaks the callstack, but in some cases you will need a small delay - play around with it and find your sweetspot :)

example:

//do small DOM manipulation here
setTimeout(function(){
  //do major DOM manuipulation here
},0);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜