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);
精彩评论