14.2 Dynamically Generated Documents
One of the most important features of the
Document object (and perhaps of client-side JavaScript in general) is
the write( ) method, which allows you to
dynamically generate web-page content from your JavaScript programs.
This method can be used in two ways. The first and simplest way to
use it is within a script, to output dynamically generated
HTML into the document that is
currently being parsed. This was discussed in Chapter 12. Consider the following code, which uses
write( ) to add the current
date and the document's
last-modified date to an otherwise static HTML document:
<script>
var today = new Date( );
document.write("<p>Document accessed on: " + today.toString( ));
document.write("<br>Document modified on: " + document.lastModified);
</script>
Using the write( ) method in this way is an
extremely common JavaScript programming technique, and you'll
see it in many scripts.
Be aware, however, that you can use the write( )
method to output HTML to the current document only while that
document is being parsed. That is, you can call
document.write( ) from within
<script> tags only because
these scripts are executed as part of the document parsing process.
In particular, if you call document.write( ) from
within an event handler and that handler is invoked once the document
has already been parsed, you will end up overwriting the entire
document (including its event handlers), instead of appending text to
it. The reason for this will become clear as we examine the second
way to use the write( ) method.
In addition to adding dynamic content to the current document as it
is being parsed, write( ) can be used in
conjunction with the open(
) and close( )
Document methods to create entirely new documents within a window or
frame. Although you cannot usefully write to the current document
from an
event handler, there is no reason
why you can't write to a document in another window or frame;
doing so can be a useful technique with multiwindow or multiframe web
sites. For example, JavaScript code in one frame of a multiframe site
might display a message in another frame with code like this:
<script>
// Start a new document, erasing any content that was already in frames[0]
parent.frames[0].document.open( );
// Add some content to the document
parent.frames[0].document.write("<hr>Hello from your sibling frame!<hr>");
// And close the document when we're done
parent.frames[0].document.close( );
</script>
To create a new document, we first call the open(
) method of the Document object, then call write(
) any number of times to output the contents of the
document, and finally call the close( ) method of
the Document object to indicate that we have finished. This last step
is important; if you forget to close the document, the browser does
not stop the document loading animation it displays. Also, the
browser may buffer the HTML you have written; it is not required to
display the buffered output until you explicitly end the document by
calling close( ).
In contrast to the close( ) call, which is
required, the open( ) call is optional. If you
call the write( ) method on a document that has
already been closed, JavaScript implicitly opens a new HTML document,
as if you had called the open( ) method. This
explains what happens when you call document.write(
) from an event handler within the same
document -- JavaScript opens a new document. In the process,
however, the current document (and its contents, including scripts
and event handlers) is discarded. This is never what you want to do,
and it can even cause some early browsers (such as Netscape 2) to
crash. As a general rule of thumb, a document should never call
write( ) on itself from within an event handler.
A couple of final notes about the write( ) method.
First, many people do not realize that the write(
) method can take more than one argument. When you pass
multiple arguments, they are output one after another, just as if
they had been concatenated. So instead of writing:
document.write("Hello, " + username + " Welcome to my home page!");
you might equivalently write:
var greeting = "Hello, ";
var welcome = " Welcome to my home page!";
document.write(greeting, username, welcome);
The second point to note about the write( ) method
is that the Document object also supports a writeln(
)
method, which is identical to the write( ) method
in every way except that it appends a newline after outputting its
arguments. Since HTML ignores line breaks, this newline character
usually doesn't make a difference, but as we'll see in a
bit, the writeln( ) method can be convenient when
you're working with non-HTML documents.
Example 14-1 shows how you might create a complex
dialog box with the Window open( ) method and the
methods of the Document object. This example registers an
onerror event handler function for the window; the
function is invoked when a JavaScript error occurs. The error handler
function creates a new window and uses the Document object methods to
create an HTML form within the window. The form allows the user to
see details about the error that occurred and email a bug report to
the author of the JavaScript code.
Figure 14-1 shows a sample window. Recall from the
discussion of the onerror error handler in Chapter 13 that Netscape 6 does not pass the correct
arguments to the error handler function. For this reason, the output
on Netscape 6 does not match what is illustrated here.
Figure 14-1. Using a browser window as a dialog box

Example 14-1. Dynamically creating a dialog window
<script>
// A variable we use to ensure that each error window we create is unique
var error_count = 0;
// Set this variable to your email address
var email = "myname@mydomain.com";
// Define the error handler. It generates an HTML form so the user
// can report the error to the author.
function report_error(msg, url, line)
{
var w = window.open(", // URL (none specified)
"error"+error_count++, // Name (force it to be unique)
"resizable,status,width=625,height=400"); // Features
// Get the Document object of the new window
var d = w.document;
// Output an HTML document, including a form, into the new window
// Note that we omit the optional call to document.open( )
d.write('<div align="center">');
d.write('<font size="7" face="helvetica"><b>');
d.write('OOPS.... A JavaScript Error Has Occurred!');
d.write('</b></font><br><hr size="4" width="80%">');
d.write('<form action="mailto:' + email + '" method=post');
d.write(' enctype="text/plain">');
d.write('<font size="3">');
d.write('<i>Click the "Report Error" button to send a bug report.</i><br>');
d.write('<input type="submit" value="Report Error"> ');
d.write('<input type="button" value="Dismiss" onclick="self.close( );">');
d.write('</div><div align="right">');
d.write('<br>Your name <i>(optional)</i>: ');
d.write('<input size="42" name="name" value=">');
d.write('<br>Error Message: ');
d.write('<input size="42" name="message" value="' + msg + '">');
d.write('<br>Document: <input size="42" name="url" value="' + url + '">');
d.write('<br>Line Number: <input size="42" name="line" value="'+line +'">');
d.write('<br>Browser Version: ');
d.write('<input size="42" name="version" value="'+navigator.userAgent+'">');
d.write('</div></font>');
d.write('</form>');
// Remember to close the document when we're done
d.close( );
// Return true from this error handler, so that JavaScript does not
// display its own error dialog box
return true;
}
// Before the event handler can take effect, we have to register it
// for a particular window
self.onerror = report_error;
</script>
<script>
// The following line of code purposely causes an error as a test
alert(no_such_variable);
</script>
14.2.1 Non-HTML Documents
When you
call the Document open(
)
method with no arguments, it opens a new HTML
document. Remember, though, that web browsers can display a number of
other data formats besides HTML text. When you want to dynamically
create and display a document using some other data format, you call
the open( ) method with a single argument, which
is the [1]
[1] This argument to the open( ) method has not
been standardized by the W3C DOM. It works in IE 4 and later, and in
Netscape 3 and 4. Surprisingly, it does not work in Netscape 6: only
HTML documents are supported by that browser.The MIME type for HTML is
text/html. The most common format besides HTML is
plain text, with a MIME type of
text/plain. If you want to use the write(
) method to output text that uses
newlines, spaces, and Tab characters
for formatting, you should open the document by passing the string
"text/plain" to the open( ) method.
Example 14-2 shows one way you might do this. It
implements a debug(
) function that
you can use to output plain-text debugging messages from your scripts
into a separate window that appears when needed. Figure 14-2 shows what the resulting window looks like.
Figure 14-2. A window for plain-text debugging output

Example 14-2. Creating a plain-text document
<script>
var _console = null;
function debug(msg)
{
// Open a window the first time we are called, or after an existing
// console window has been closed
if ((_console == null) || (_console.closed)) {
_console = window.open(","console","width=600,height=300,resizable");
// Open a document in the window to display plain text
_console.document.open("text/plain");
}
_console.focus( ); // Make the window visible
_console.document.writeln(msg); // Output the message to it
// Note that we purposely do not call close( ). Leaving the
// document open allows us to append to it later.
}
</script>
<!-- Here's an example of using this script -->
<script>var n = 0;</script>
<form>
<input type="button" value="Push Me"
onclick="debug('You have pushed me:\t' + ++n + ' times.');">
</form>