home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


JavaScript: The Definitive GuideJavaScript: The Definitive GuideSearch this book

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

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">&nbsp;&nbsp;');
    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 MIME type you desire.[48]

[48]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

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>


Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.