What is the difference between the write function and the innerHTML property? There is a huge difference. What happens to any JavaScript you import using these methods? It’s more complicated than you think.
The write() function (as in, document.write), writes text to the “document.” When an HTML page loads, what’s actually happening is a “document” is opened, some content is written to it, and then it is closed. This “document” is the main HTML window that you end up seeing. When a document is opened and written to, everything inside it is cleared out. Let me restate that: if you try to use document.write() after the HTML page has finished loading, you will destroy everything on the page, including the JavaScript tags, all the HTML, and pretty much everything you would see if you hit “view source.” Granted, if you did look at the source, you’d still see the original HTML that was loaded since the “view source” command shows you the originally loaded content, not the result of any JavaScript changes.
document.write(‘this will erase everything if it is called after the page finishes loading!’);
On the other hand, innerHTML changes the contents inside a given element. For example, if you have an HTML tag such as a “span”, its innerHTML refers to the contents between the opening and closing tag, not including the tag itself. Note that innerHTML stuff can not happen if the HTML element has not finished loading! If you try to modify the innerHTML of content that appears below the script, you will get an error.
<script>
// THIS WILL CAUSE AN ERROR, UNDEFINED ELEMENT!
document.getElementById(‘the-div’).innerHTML = “changed!”;
</script>
<span id=”the-div”>click me</span>
<span id=”the-div”>click me</span>
<script>
// works great, “click me” will be changed to “changed”
document.getElementById(‘the-div’).innerHTML = “changed!”;
</script>
<body>
<span onClick=”loadContents(‘target-div’, somePage)”>click me</span>
<div id=”target-div”></div>
</body>
Notice how in that last example, I use a target-div parameter, even if that target is blank and at the end of my body section. Many less experienced coders might have used document.write to try and append text to a page after things are finished loading. You can now understand why that’s a bad idea and why weird things might happen if you do.
Lastly, any JavaScript you import into your document will not be executed. This is part of JavaScript’s security model. You must run an “eval()” on the contents. Additionally, any functions you declare should use the following syntax:
yourFunctionName = function() {
// function contents;
}
This is to ensure yourFunctionName is globally accessible. See, the problem is that often times you will be doing innerHTML calls from within another function. For example, when someone clicks on some text, you might dynamically grab some other content (AJAX!) and load it in. That content might have some JavaScript you want to load. This would all be done through a trusty method you named “loadContents” or something.
// Example contents from somePage (bad version)
function myFunction() {
alert(‘stuff loaded!’);
}
// Fictitious loadContents function that gets AJAX content
function loadContents(targetDIV, somePage) {
var content = getContentsUsingAJAX(somePage);
// load JavaScript into innerHTML of the target
var jsContent = parseOutJavaScript(content);
document.getElementById(targetDIV).innerHTML = eval(jsContent);
}
Notice that this applies for getting content via an AJAX type of method. This is because it is common for a retrieved page to contain script tags which may need to be evaluated. The problem is that all JavaScript you eval() inside this “loadContents” function becomes trapped in the glass room commonly known as “local scope.” If I were to do this literally, the above two examples become:
function loadContents() {
function myFunction() { // loaded from somePage
alert(‘stuff loaded!’);
}
}
This is called an “inner function,” something that may be too complex to summarize very well. But here goes. An “inner function” is a function (B) that is inside another function (A). It is a local function (B) to that function (A). In other words, it (B) is not a globally accessible from outside of the parent function (A). As such, other functions can have their own inner functions with the same name without naming conflicts. Arg, I think my explanation isn’t clear.
This is an (unrelated) example for an inner functions:
function sayHi() { // this would be (A)
function sayHiAgain() { // this would be (B)
alert(‘hi again’);
}
alert(‘hi’);
}
sayHi(); // displays “hi” only
function sayHi() {
function sayHiAgain() {
alert(‘hi again’);
}
alert(‘hi’);
}
sayHiAgain(); // error, undefined function
Calling sayHiAgain() would result in an undefined function error because sayHi() needs to be called first to define sayHiAgain. But that alone won’t solve the problem, as the next two examples will show:
function sayHi() {
function sayHiAgain() {
alert(‘hi again’);
}
alert(‘hi’);
sayHiAgain(); // this works.
}
sayHi(); // displays “hi”, and then “hi again”
function sayHi() {
function sayHiAgain() { // destroyed at the end of sayHi()
alert(‘hi again’);
}
alert(‘hi’);
}
sayHiAgain(); // not yet defined until sayHi is called
sayHi(); // displays “hi” only
sayHiAgain(); // still an error because it no longer exists
Calling sayHiAgain inside the sayHi() function will make “hi again” display, as expected. But the final example fails because even though sayHiAgain() is being defined when sayHi() is called, it fails because when sayHi() finishes running, sayHiAgain gets destroyed (the glass room gets smashed). The following will ALSO fail.
function sayHi() {
var sayHiAgain = function() { // destroyed at the end of sayHi()
alert(‘hi again’);
}
alert(‘hi’);
}
sayHi(); // displays “hi” only
sayHiAgain(); // undefined error
We will revisit this example in a moment. Back to our original example.
All functions that are defined inside loadContents() become accessible only from within loadContents(). This means as soon as loadContents() finishes, your newly loaded JavaScript gets the axe. This will cause horrible, strange bugs, as you can imagine.
In order to get around this, we rely on JavaScript’s commonly hated automatic global “variable-ization” of everything. The imported JavaScript would consist of functions assigned to global variables. Those global variables would exist just fine after loadContents() finishes.
If you use “var blahblahblah” inside a function, you create a variable called blahblahblah. However, if you omit the use of “var,” blahblahblah becomes a global variable! The following two examples, therefore, work:
function sayHi() {
sayHiAgain = function() { // no more “var”
alert(‘hi again’);
}
alert(‘hi’);
}
sayHiAgain(); // error, undefined
sayHi(); // displays “hi” only
sayHiAgain(); // displays “hi again”
// Example contents from somePage (good version)
myFunction = function() {
alert(‘stuff loaded!’);
}
// myFunction is now a global variable!
You can call these global variable function thingies just like you would any normal JavaScript function:
myFunction();
That will call the myFunction function defined inside loadContent().
And that’s the terrible secret of dynamic JavaScript!
Disclaimer: I did not test any of the example code. Please disregard lame typos.
Simply call the function with parameters. i don’t understand… myfunction(a, b) would work.
I got your idea. But how to use it, when the function has parameters? Please explain me, as i am trying it out for a long time.
Michi,
Thanks for the information on this page. I have spent 6 days (yep I’m not very good!) trying to work my way through these issues by using such resources as your site. Unfortunately, most were of little help (more of a hindrance!), but yours was clear, non-geeky and best of all worked!
Before reading the above I though I only needed to know the correct way of eval’ing my script (the SCRIPT tags were the problem!!) but thanks to this I have also sorted out the function scoping issues before I even knew they were a problem. Cheers.
Mike: It is possible my JS is not 100% perfect. I wrote the code off of the top of my head and only tested it minimally. =) But the overall concept will work in most browsers, including IE. Also, make sure you are not copy-pasting those slanted quotes around my code… JS will not like those.
Jen: Please note that browsers WILL SEE the JavaScript. The problem is that they will not execute the text. In other words, your JS code is treated like plain text. That said, having one div read another will work. Make sure the code to read the JS is working. Finally, the JS text must be pushed through an eval() call. In other words: eval(getTheDivContents()); Note that the eval function wants JavaScript, not HTML so do not include in script tags.
You said:
“Lastly, any JavaScript you import into your document will not be executed. This is part of JavaScript’s security model. You must run an “eval()†on the contents.”
This is where I’m having a problem. I need to load content (an HTML page) into a “dynamic div” using innerHTML. From the page that loads, I want to execute another function to load content FROM that page to ANOTHER “dynamic div” … but since javascript won’t be seen from within the imported document, how could I go about this????
ANY HELP IS GREATLY APPRECIATED! THANKS!!!
~Jennifer
not working in IE 6, do you have any idea on this ?