JavaScripting Netscape 6: No More Sloppy Code | 5
[previous][next] |
JavaScript Tips and Tricks
Objects/Functions defined in Dynamically Loaded Scripts are not defined until the Script block completes
NS6, Mozilla, and IE5.5+ have all changed their behavior related to the loading of external JavaScripts that are dynamically written.
The Problem
Calling a function defined in a dynamically loaded script within the same script block that loads the dynamic script does not work in NS6 or IE 5.5. See some test cases of this behavior and Peter Belesis' discussion about loading external JS files at the end of this article.
This old way doesn't work:
<script language="javascript">
// newsflipper.js defines a function init()
document.write('<script language="javascript" src="newsflipper.js"><\/script>');
window.onload = init; // this could also be a call to init();
</script>
The Solution
NS6 and IE5.5 need to have the first script block closed first. The onload handler or function call should then be defined in a subsequent script block in order for functions or objects to be defined, as follows:
<script language="javascript">
// newsflipper.js defines a function init()
document.write('<script language="javascript" src="newsflipper.js"><\/script>');
</script>
<script language="javascript">
window.onload = init;
</script>
NS6 and IE 5.5 see the script tag and first parse and compile its contents. Once the contents of the script tag are parsed and compiled, NS6 and IE5.5 start to execute the contained script. When NS6 and IE execute the above document.write,
document.write('<script language="javascript" src="newsflipper.js"><\/script>');
they do not "insert" the newsflipper.js script into the already parsed
script block and complile it "in place." Instead, they start loading the
external file, but continue executing the current script block. Therefore
variables or objects that are defined in the external
script file that is dynamically loaded will not be available until the next script block.
Once NS6 and IE 5.5 have finished executing the script block where the script tag with a SRC attribute was written out, they finish loading the newsflipper.js script file, parse it and then begin to execute it. This behavior has several benefits for the browser implementors. Consider the difference in how the browser would handle the case where a page dynamically included an external file that dynamically included an external file that...
Another Solution?
One other possible workaround would be to use an intermediate "loader" file that does what the above code does, as follows:
<script language="JavaScript1.2" src="ftestload.js"></script>
that calls floadtest.js below:
<SCRIPT LANGUAGE="JavaScript1.2" type="text/javascript">
<!--
document.write("<SCRIPT LANGUAGE='JavaScript1.2' SRC='newsflipper.js'><\/SCRIPT>");
//-->
</SCRIPT>
This kludge works in NS6! However, there is a problem with Netscape 3. It displays the external JavaScript file "ftestload.js" as text if you don't have your MIME types set up properly on your server. The Netscape Developer site states the solution to this problem -Â "the server must map the .js suffix to the MIME type application/x-javascript, which the server sends back in the HTTP header":
https://developer.netscape.com/docs/manuals/js/client/jsguide/embed.htm#1013277
Thanks to John Kane for this workaround.
So you can see the need for the indirect document.write of the SCRIPT SRC tag. The ultimate solution is to make sure your onload handler is defined either in the same external file, or in a subsequent <SCRIPT> block. However, there is a related problem with executable code that refers to HTML elements within your page, which we'll discuss next. Our newest flipper uses the techniques discussed in this article.Element-dependent Executable Code defined in Dynamically Loaded Scripts is not defined until the HTML page loads
The Problem
Our testing confirmed that NS6 executes external JavaScript files while the page is rendering so any executable code that relies on defined HTML elements in your page will try to reference an undefined element, causing an error. The solution is to move any executable code that references elements within your page inside a function called onload. This defers execution of this element-dependent code to a point after the page has rendered. Initial tests showed that other executable code like array definition or default variables can safely remain outside functions as they don't reference HTML elements within the page. This technique of moving element-dependent code inside an onload function fixed the problem of NS6 hanging for us in our news flipper code.
Here's the flipper before:
function FDRcountLoads(){
...
}
// executable code refers to HTML elements within the page. In
// NS6/IE5.5 this technique doesn't work anymore, as the script
// executes before the element is defined, spawning an error, or
// worse, doing nothing.
if (DOM) {
DOMfad1 = document.getElementById('IEfad1');
DOMfad1.style.height=DOMfad1.offsetHeight + "px";
DOMfad1.onmouseover = FDRmouseover;
DOMfad1.onmouseout = FDRmouseout;
}
The Solution
The solution is to put your element-dependent code within a function called onload.
And after:
function FDRcountLoads(){
// don't put executable code *that depends on elements being
// defined* outside of an onload function, for in NS6/IE5.5 the .js
// files are rendered at same time the page is displayed, so any
// code that depends on HTML elements being defined doesn't work
//
// so move any code that looks for HTML elements in onload function
// then ns6 ie5 will work, as element will be defined onload
if (DOM) {
DOMfad1 = document.getElementById('IEfad1');
DOMfad1.style.height=DOMfad1.offsetHeight + "px";
DOMfad1.onmouseover = FDRmouseover;
DOMfad1.onmouseout = FDRmouseout;
}
}
[previous][next] |
Created: February 15, 2001
Revised: Aug. 15, 2001