Some Remarks about Ajax-includes using document.write
by
, 02-27-2010 at 07:19 PM (43718 Views)
Some Remarks about Ajax-includes using document.write
I. Why document.write?
Including external content with the help of document.write brings in external html AND external code (js, css). This is a clear advantage of document.write over other inclusion-methods, like innerHTML (does not execute scripts) and appendChild (does not automatically import all the external js and/or css when IE, Chrome or Opera are used). To see this, create a file external.html with some js in it, then try to include it using the following. You'll see that document.write makes the external code (code of the external file) work in all browsers (but see II), that innerHTML refuses to execute the external code in any browser, and that appendChild doesn't execute it in Chrome, IE and Opera:So if we use Ajax to insert a HTML-menu into our page, (document.)writing the responseText seems to be a good option (see IV below, however, for some remarks on the 'evil nature' of document.write).Code:<head> <script type="text/javascript"> function HttpRequest(url){ var pageRequest = false //variable to hold ajax object /*@cc_on @if (@_jscript_version >= 5) try { pageRequest = new ActiveXObject("Msxml2.XMLHTTP") } catch (e){ try { pageRequest = new ActiveXObject("Microsoft.XMLHTTP") } catch (e2){ pageRequest = false } } @end @*/ if (!pageRequest && typeof XMLHttpRequest != 'undefined') pageRequest = new XMLHttpRequest() if (pageRequest){ //if pageRequest is not false pageRequest.open('GET', url, false) //get page synchronously pageRequest.send(null) //doing this for appendChild var newdiv = document.createElement("div"); newdiv.innerHTML = pageRequest.responseText; if(/*@cc_on!@*/false) {newdiv.innerHTML.text= newdiv.innerHTML;} document.write(pageRequest.responseText); //document.body.appendChild(newdiv); //document.body.innerHTML+=pageRequest.responseText; } } </script> </head> <body > <script type="text/javascript">HttpRequest("external.html")</script>
II. Problems with document.write
But using document.write across browsers is not without problems. There's a difference indeed between IE and non-IE as far as executing the imported scripts is concerned. In IE, external scripts are executed after internal scripts. So if we havewhere b.js is supposed to (document.)write 'b', we get 'acb' in IE. But in non-IE, we get 'abc', because non-IE browsers simply execute scripts in the order in which they are presented, without distinguishing between internal and external scripts. Now, the non-IE way of executing scripts in includes that are done with document.write turns out to be non-problematic, whereas the IE way does produce unwanted results, see this. So we must force IE to behave like non-IE.Code:<script type="text/javascript">document.write('a')</script> <script type="text/javascript" src="b.js"></script> <script type="text/javascript">document.write('c')</script>
III. An easy solution
This can be done rather easily: all the scripts of the external file we want to include must be (made) either internal or external. Obviously, the second option must be preferred. This means that, if we want to include a file using document.write - whether we do it with Ajax or with the help of another technique - we must first make sure that all the scripts of the file_to_be_included are (made) external.
IV. But document.write is evil, isn't it?
I argued here that the use of a string (responseText, in this case) for representing not just text, but also code, is bad practice. When I wrote that page (about Segmented Includes) I wasn't aware of the fact that the problems I described there were due to the way IE handles internal and external scripts having document.write (see II-III above). Those problems seem to disappear when we do as indicated in III. This being said, I must admit that using strings (like the ones produced by document.write) for representing code will always be bad practice. But as longs as bad practice does not produce bad results, as in the cases described here, we can use it (reluctantly, maybe).
V. But document.write cannot be used for dynamic updates, right?
Document.write cannot be used for including external content after the page has loaded. So we can do <script type="text/javascript">document.write('something')</script> but not <script type="text/javascript"> function do_something(){document.write('something')}</script> and then call function do_something() with an onload, an onclick, an onmouseover, or whatever: in stead of adding something to the page, we would wipe out its entire content and simply replace it with something. So, if we are using document.write, we can't dynamically add something to our document (let alone put it at a certain position) after the page has loaded. But we can fake it, see this.
VI. Conclusive remark
So if you're happy with the (fake) solution given in V for dynamically updating your page with the help of document.write, use it. It has the clear advantage of automatically importing external js and css, and isn't perhaps that bad, if we take into account what has been said in III.
Addendum:
There are lots of Ajax-include scripts on the Internet. But many of them cannot be used locally (on the hard disk) when IE (8) is used. The one given above can be used locally. The script provided here by DynamicDrive cannot ('access denied'). Curiously, the following script, found at a page that favoribly refers to the DD-script, can be used locally (using IE8):DEMO HERE.Code:<script type="text/javascript"> /* found at http://www.javascriptkit.com/dhtmltutors/ajaxincludes.shtml */ function HttpRequest(url){ var pageRequest = false //variable to hold ajax object /*@cc_on @if (@_jscript_version >= 5) try { pageRequest = new ActiveXObject("Msxml2.XMLHTTP") } catch (e){ try { pageRequest = new ActiveXObject("Microsoft.XMLHTTP") } catch (e2){ pageRequest = false } } @end @*/ if (!pageRequest && typeof XMLHttpRequest != 'undefined') pageRequest = new XMLHttpRequest() if (pageRequest){ //if pageRequest is not false pageRequest.open('GET', url, false) //get page synchronously pageRequest.send(null) embedpage(pageRequest) } } function embedpage(request){ //if viewing page offline or the document was successfully retrieved online (status code=2000) if (window.location.href.indexOf("http")==-1 || request.status==200) document.write(request.responseText); } </script>
===
Arie.