/    Sign up×
Community /Pin to ProfileBookmark

external javascript breaks document.write() recursion

Hi there,

This is our problem:

We write a new page xyz to the current browser window by using the well known command sequence

[CODE]document.open();document.write(xyz);document.close();[/CODE]

.
If now the string [FONT=Courier New]xyz[/FONT] itself contains a javascript tag which references an external JS file Γ  la

[CODE]<script src=”http://somewhere/extern.js”></script>[/CODE]

, further [FONT=Courier New]document.write()[/FONT] commands, contained in the xyz string or even in the external JS will be extecuted by the browser not until the complete xyz page was processed.
This means that the output of those [FONT=Courier New]document.write()[/FONT] commands will not reach the proper location inside the xyz page. The Internet Explorer sticks them to the end of the page, while Firefox as its predecessor Netscape4, shifts in – as well known – a [FONT=Courier New]document.open()[/FONT] before the delayed [FONT=Courier New]document.write()[/FONT] effecting that the output is brought onto a new blank page. (Secondary Firefox sits there on the blank page waiting for a [FONT=Courier New]document.close()[/FONT] which will never come from us since we never caused this [FONT=Courier New]document.open()[/FONT] 😑 )

This should demonstrate the above:
— File ‘loader.html’ —-

[CODE]<html>
<head>

<script language=”JavaScript”>
function writeNewPageIntern()
{
document.open();
document.write(‘<html><body><h1>WrittenPage Title</h1><hr><script language=”JavaScript”> document.write(“dynamic contents (inside string)”)</script><hr></body></html>’);
document.close();

}

function writeNewPageExternJS()
{
document.open();
document.write(‘<html><body><h1>WrittenPage Title</h1><hr><script language=”JavaScript” src=”http://localhost/test/extern.js”></script><hr>end of page</body></html>’);
document.close();

}

function writeNewPageExternJSIntern()
{
document.open();
document.write(‘<html><body><h1>WrittenPage Title</h1><hr><script language=”JavaScript” src=”http://www.no-such-server.ddr/halb/Cgi/extern.js”></script><script language=”JavaScript”> document.write(“dynamic contents (inside string)”)</script><hr>end of page</body></html>’);
document.close();
}

</script>

</head>
<body>

<h1>LoaderPage Title</h1>

<form action=”javascript:writeNewPageIntern()”><input type=”submit” value=”writeNewPageIntern()”></form>

<form action=”javascript:writeNewPageExternJS()”><input type=”submit” value=”writeNewPageExternJS()”></form>

<form action=”javascript:writeNewPageExternJSIntern()”><input type=”submit” value=”writeNewPageExternJSIntern()”></form>
</body>
</html>[/CODE]

— File end —

— File extern.js —

[CODE]document.write(“dynamic contents (extern JS)”);[/CODE]

— File End —

The first function ([FONT=Courier New]writeNewPageIntern()[/FONT]) is successful and demonstrates that recursive calls of [FONT=Courier New]document.write()[/FONT] are troublefree.

The second function [FONT=Courier New]writeNewPageExternJS()[/FONT] shows the problem with document.write() commands in the external JS file. [I](Please adjust in [FONT=Courier New]writeNewPageExternJS()[/FONT] the location of the external JS file on your system.)[/I]

The third function [FONT=Courier New]writeNewPageExternJSIntern()[/FONT] demonstrates that recursion is broken for Mozilla even if the external JS can not be found.

Has anyone already encountered such behaviour?

A straight-forward workaround could consist in preloading the external JS files somehow in strings and insert them into the xyz string before we do our [FONT=Courier New]document.write(xyz)[/FONT]. ? We will try this now and maybe I’ll post later whether this helps.

Best regards
Semmel

to post a comment
JavaScript

18 Comments(s) ↴

Copy linkTweet thisAlerts:
@gil_davisAug 25.2006 β€”Β This is fairly well documented behavior. The browser sees the <script> tag before it is done writing it and gets confused. To avoid the problem, break up the tag:
<i>
</i>document.write('&lt;html&gt;&lt;body&gt;&lt;h1&gt;WrittenPage Title&lt;/h1&gt;&lt;hr&gt;&lt;scr' + 'ipt language="JavaScript"&gt; document.write("dynamic contents (inside string)")&lt;/scr' + 'ipt&gt;&lt;hr&gt;&lt;/body&gt;&lt;/html&gt;');
Copy linkTweet thisAlerts:
@SemmelauthorAug 25.2006 β€”Β This is fairly well documented behavior. The browser sees the <script> tag before it is done writing it and gets confused. ...[/QUOTE]

Hi Gil,

I can not support your argumentation, since the code in my first function [FONT=Courier New]writeNewPageIntern()[/FONT] works perfectly. You are right in that the browser gets confused, but it would be because of a [B]closing[/B] [FONT=Courier New]</script>[/FONT] tag. Thats why one writes here [FONT=Courier New]</script>[/FONT]. Check the page source of the newly written page there you'll find that the HTML code has arrived as desired.

All I am saying is that, if there is anywhere in the new page a reference to an [B]external[/B] [B]javascript file[/B], like in my second and third function, the execution of the included script code (especially further calls to [FONT=Courier New]document.write()[/FONT]) gets delayed after the complete rendering of the new html page itself.

Semmel
Copy linkTweet thisAlerts:
@gil_davisAug 25.2006 β€”Β Sorry, brain cramp. ?

Issuing document.open() on a closed document erases the existing content. Further, issuing a document.write() after the document has been closed will initiate a document.open() as well, which again erases the existing content. It really has nothing to do with the external js files.

Basically, using document.write() on the current window after the initial load (i.e.: after the onload event occurs) will effectively erase your document. The browser will complete the current line of code and not be able to execute the next line because it will be gone.

Execution may continue into the external script since it is being loaded into the now empty document due to the speed of the browser and its threading architecture. That is probably why the first external script seems to function in IE, and not in FF.

If you execute your code in a different window, it should work as you expect. You could even do it in a frame or iframe to avoid opening another browser instance. But you will not be able to make it work in the same window.
Copy linkTweet thisAlerts:
@SemmelauthorAug 25.2006 β€”Β I agree to what you write about [FONT=Courier New]document.write(), document.open()[/FONT].
... Basically, using document.write() on the current window after the initial load (i.e.: after the onload event occurs) will effectively erase your document. The browser will complete the current line of code and not be able to execute the next line because it will be gone.[/QUOTE] This [I]"next line"[/I] you are writing about will be simply the [FONT=Courier New]document.close()[/FONT] command in the three functions. No problem here in my design at all, since the original script's only purpose is to load the new page via [FONT=Courier New]document.write()[/FONT] - nothing more.

I can not agree with you here:
If you execute your code in a different window, it should work as you expect. You could even do it in a frame or iframe to avoid opening another browser instance. But you will not be able to make it work in the same window.[/QUOTE] It has apparently nothing to do to which window the [FONT=Courier New]document.write()[/FONT] call is directed: I took the failure code which uses an external JS file from my second function, but now I write to a completely new window:[CODE]window1 = window.open("","window1", "HEIGHT=300,WIDTH=300");

window1.document.write('<html><body><h1>WrittenPage Title</h1><hr><script language="JavaScript" src="http://localhost/extern.js"></script><hr>end of page</body></html>');
window1.document.close(); [/CODE]
This code [B]fails again[/B] if an external JS is referenced. (If I exchange the external JS with my inline code as in the first function this is again successful.)

[B]I find no difference to which window I write to, be it the current window itself or a newly opened one.[/B]

Execution may continue into the external script since it is being loaded into the now empty document due to the speed of the browser and its threading architecture. That is probably why the first external script seems to function in IE, and not in FF.[/QUOTE] Right, this could be the underlying reason. However I find that [B]both browsers[/B] show this behaviour, but IE sticks the delayed output of external JS [FONT=Courier New]document.write()[/FONT] to the end of the page, while Firefox puts it on an automatically opened blank page and waits there for [FONT=Courier New]document.close()[/FONT]

Good discussion so far, I hope we eventually get through.

Semmel
Copy linkTweet thisAlerts:
@gil_davisAug 26.2006 β€”Β Have you tried splitting up each occurrence of the word "script" within the document.write(), as in my first suggestion? I haven't done much document.write() stuff in the newer (> 4.0) browsers since the advent of DOM, but that was *always* a problem in the past.
Copy linkTweet thisAlerts:
@dchasmanAug 31.2006 β€”Β Semmel,

For what its worth I am also beating my head against a wall on this exact issue (my case is writing to an iframe's document content that uses the yahoo maps api - others have this problem with this api (see http://particletree.com/notebook/dynamically-include-yahoo-maps)). I went through all of the same steps/attempts to fix this and so far still no joy. I am going to try Gil's suggestion on the breakup of <script> but I only have access to the first level of dynamic <script> tags that ref external sources so I expect that even if this works I'll only be solving the case for 1 hop away only (not the arbitrarily nested situation).

Doug
Copy linkTweet thisAlerts:
@dchasmanAug 31.2006 β€”Β As expected breaking the <script> tags did not help ?

I am willing to try anything short of witchcraft at this point...
Copy linkTweet thisAlerts:
@SemmelauthorOct 12.2006 β€”Β To wrap it up,

eventually we practically resigned! :mad:

We implemented an evil workaround, consisting in [B]making the external scripts internal[/B]:

In out special case the the string to be written by the outermost (the first) document.write command is available on the web server. So we came up with some fancy search patterns to locate all [FONT=Fixedsys]<script language="JavaScript" src="http://fancyserver.com/extern.js"></script>[/FONT]-like occurences in the string. In the second step our web server turns into a browser and pre-loads all that external JS and other string variables. In a third step, if those strings contain document.write commands, the search matches of the external scripts in the first string get replaced by the pre-loaded scripts.

Thereby we are left with an html page to write which contains just internal scripts.

However if those pre-loaded external scripts themselves reference external scripts one has to pre-load those as well..

The crunch is that when our web server queries those external JS, it might not receive the same answer as if done by a web browser. This is since those external JS might very well be dynamically generated depending on cookies, sessions or whatever.

So this workaround is surely not a general solution.

Semmel
Copy linkTweet thisAlerts:
@arielgOct 15.2006 β€”Β If it helps anyone - I am breaking the head with the same problem. I am trying to write a greasemonkey script that loads the current page from the server, rewrites it abit, and then writes it back using document.open().write(page).close(); but when an external script is involved, the document.close crashes the page, and I only get a blank page. If I don't call document.close the page does all render, but does not close.

If someone else has any thought it would be helpful.
Copy linkTweet thisAlerts:
@felgallOct 15.2006 β€”Β Using the appropriate DOM commands to create the new web page content would involve writing a lot more code but would avoid all of the problems associated with using document.write.
Copy linkTweet thisAlerts:
@arielgOct 15.2006 β€”Β Well, after playing with it just abit more, it first seems that using document.close does not actually break all pages with external javascript. Some pages i checked didn't suffer from this problem. I found a workaround solution - closing the document with document.close after a second or so using window.setTimeout. This solved the problem.
Copy linkTweet thisAlerts:
@mswOct 17.2006 β€”Β Hi! I have same problems, but not only with JS files, but also with images ? I.e. if you try to insert few images, like
[CODE]document.write('<img src="..."> .... <img src=" ">)[/CODE], you will have same issue... I spend all time today to fix this, but without happy end... My work is to create dinamically IFRAME and fill it with some content, that taken via AJAX from server...

First i fill div with IFRAME like this:
[CODE]$(this.name+this.id+ '_'+ this.iterator + '_div').innerHTML = '<IFRAME name="myframe'+this.name.toString()+this.id.toString()+'"width="'+this.xs.toString()+'px" height="'+this.ys.toString()+'px" marginheight="0" marginwidth="0" hspace="0" vspace="0" frameborder="0" src="javascript: ;"></IFRAME>';
[/CODE]

than i open this frame for writing:

[CODE]parent.frames["myframe"+this.name.toString()+this.id.toString()].document.open();[/CODE]

than i write some html to my frame: [CODE]parent.frames["myframe"+this.name.toString()+this.id.toString()].document.write(this.html);[/CODE]

than i close this frame: [CODE]parent.frames["myframe"+this.name.toString()+this.id.toString()].document.close();[/CODE]

BUT! If html contain only "static" data, than all is ok... If html contain with [CODE]<img src> or <script src> [/CODE]or something, that needs to be load from server, i have BIG trobles in Firefox...

I try setTimeout, but this is not correct fix, because we don't realy know how many time we need to wait, until content is downloaded... (setTimeout (xxx,0) or setTimeout (xxx) don't fix too)

The solution to preload all data i think not so simple and can in future have some other issues...

I don't see for now 100% fix of this issue in FireFox... In IE there is property document.readyState (for example, you can use check "complete" value of this property... etc), but Firefox don't have :mad:

Also, i don't for now realy understand how we can do all this, without "open, write, close" that [B]felgall[/B] write: "Using the appropriate DOM commands to create the new web page content would involve writing a lot more code but would avoid all of the problems associated with using document.write."... If any have success with this, please post there way to do it...
Copy linkTweet thisAlerts:
@felgallOct 17.2006 β€”Β Compare the code for the script at http://javascript.about.com/library/bldtime1.htm which uses DOM commands with the code for the script at http://javascript.about.com/library/bltime1.htm which uses document.write to write the content originally and innerHTML to update it (in both cases the script can be downloaded from the second page). Both scripts do much the same thing (producing the text in red at the bottom of the page) so most of the differences in the code are the sorts of changes you need to make to use the proper DOM commands to update the web page.
Copy linkTweet thisAlerts:
@mswOct 17.2006 β€”Β Sorry, Stephen, but i think you miss something in our discussion... You in your js code use innerHTML function and it works, BUT! it only fill "preloaded" content to some element.... Its ok, i give you example:
[CODE]<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
</head>

<body onload="start();">
<div id="mydiv">TEST DIV</div>

<script>
function start() {
document.getElementById("mydiv").innerHTML = '<IFRAME name="myframe" width="100px" height="100px" marginheight="0" marginwidth="0" hspace="0" vspace="0" frameborder="2" src="javascript: var i=0;"></IFRAME>';
parent.frames["myframe"].document.documentElement.innerHTML = '<B>I like programming...</B><img src="images/myimage.gif" />';
}
</script>
</body>
</html>
[/CODE]


First i create DIV element id="mydiv"

Then i fill it via innerHTML with IFRAME name="myframe"

Then i fill body of "myframe" with some content... "I like programming..." and images.

Problem is that "preloaded" content such as <B>I like programming</B> is appears, but images - NOT! This is because when i replace content of body JS only RE render existing data with new addons, but NEWER try to reload this from server....

If you then to document.write and then document.close, command document.close will RE render all and download images and other not "constant" objects....

So, we can use innerHTML, but only to create dynamicaly such elements of page, that don't needs to load some content from server...( as images for example...)

As i said above, i think for now i and you don't have solution, like via innerHTML or via open/write/close, to create dynamicaly IFRAME or other objects, that must download some data from server...

I think maybe we can use other idea... We can create some inline elements by own hand via createDocument() and if there is some way to insert this to not IFRAME, but maybe to OBJECT for example... i don't know for now...

BUT I REALY NEED SOLUTION FOR THIS ?

P.S. I try to insert some html to IFRAME, because have one simple task: i load data dynamicaly via AJAX from server and insert it to many <div> (in past) or to many <iframe>... with <div> all is ok, because this <div> exist in current document... with iframe i fail, because every iframe use own document ? But this is realy greate, because as every iframe use own document, it use own NAMESPACE for all veriables and "id", "name" of every element... its like every iframe live in own house, but if he want to take some data from White House, he only need to say like: top.xxxx and that is all ?

Also sorry for my bad english and for long message with to many repeats ?

Sorry, some small add: my code don't works with Firefox, but with small changes he works fine in IE and AWANT:
[CODE]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
</head>

<body onload="start();">
<div id="mydiv">TEST DIV</div>

<script>
function start() {
document.getElementById("mydiv").innerHTML = '<IFRAME id="myframe" name="myframe" width="100px" height="100px" marginheight="0" marginwidth="0" hspace="0" vspace="0" frameborder="2" src="javascript: var i=0;"></IFRAME>';
document.getElementById("myframe").document.body.innerHTML = '<B>I like programming...</B><img width="50px" height="50px" src="register.gif" />';
}
</script>
</body>
</html>
[/CODE]


In Opera it fails like in Firefox, i.e. image don't appear....

So it looks, like innerHTML work's VERY different in every browser (Firefox+Opera or IE+Avant)
Copy linkTweet thisAlerts:
@felgallOct 17.2006 β€”Β You are looking at the wrong version of my script. The old outdated version of my script uses document.write to write the initial content and innerHTML to update it - that version of the script will not work in XHTML and will probably have problems in future browser versions. The new version of the script does it properly using DOM calls and so does not need to use either document.write or innerHTML. It also does away with the need to have any Javascript within the web page itself. I left the old version on the site so that people can compare the two scripts to see how outdated code using document.write and innerHTML can be rewritten to use the correct standard code that performs the job of updating the page content correctly.
Copy linkTweet thisAlerts:
@mswOct 17.2006 β€”Β Sorry, you miss again ? You in your new script use W3C DOM ? Problem is not there... try to display in some created element in your script, for example IMAGE!

"obj.firstChild.data = setDsp(mtz,dst,stdz,dayz,showDate); " this is line from your code! setDsp return txt, this is why you can use .data=... and this works! But if you want to display in firstChild for example image, you can't do this by data... you must use innerHTML or something same... and if this "data" (realy NOT just html text!) contain something like '<img src="gggg.gif">' you will fail with this... i.e. NO picture will be inserted to element! NO!

Even if you will do like:
[CODE]var picture = parent.frames["myframe"].document.createElement("img");
picture.src = "register.gif";
parent.frames["myframe"].document.body.insertBefore(picture, parent.frames["myframe"].document.body.firstChild);
[/CODE]

you will FAIL! Only border of picture you will see in Firefox ?

(note! I use there only your favor W3C DOM ? )
Copy linkTweet thisAlerts:
@mswOct 17.2006 β€”Β "small" addon: not only JS, IMAGES etc don't works if it is loaded dynamicaly to IFRAME, but also no Events working! this also don't working in Firefox... (i am not 100% sure with this, but in first look it is)

Looks like we need to insert only HTML "static" elements to IFRAME... all other, like JS, IMG etc needs to be downloaded via AJAX ? and executing via eval() function... or maybe other way...

Stupid situation... ? ? ?

Any ideas?
Copy linkTweet thisAlerts:
@felgallOct 18.2006 β€”Β In that script of mine I was going to leave the innerHTML reference there but it doesn't work in that instance because the tag it was trying to update was added to the document via the DOM and therefore the innerHTML was unable to reference it. Mixing DOM and innerHTML appears to give problems easily solved by using either one or the other. You should be able to avoid using document.write though simply by using one of the other two to update the page after that part of the page content has loaded.


Try removing the [b]parent.frames["myframe"].[/b] from the first line of the image code as that should not be there in defining the image tag and is only required on the third line when you tell it where to add it.
Γ—

Success!

Help @Semmel spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 β€” BETA 6.17,
whats_new: community page,
up_next: more Davinciβ€’003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinciβ€’003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @nearjob,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,
)...