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


JavaScript: The Definitive Guide

Previous Appendix B
Known Bugs
Next
 

B.3 Commonly Encountered JavaScript Bugs in Navigator 2.0

Navigator 2.0 has a lot of bugs. This is a fact of life and a source of frequent frustration. By being aware of the most important and most frequently encountered bugs, you can begin to reduce the amount of frustration you'll have to endure when programming with the Navigator 2.0 version of client-side JavaScript--and, more important, the amount of frustration your customers endure when they run your JavaScript code with Navigator 2.0.

If you're wondering why this relatively long section has been devoted to Navigator 2.0 bugs, when presumably these have all been fixed in Navigator 3.0, remember that it doesn't matter what version of Navigator you run; it is the user's version that counts. Even with Navigator 3.0 released in final form, your scripts may still be run on many Navigator 2.0 platforms.

Navigator 2.0 is sufficiently buggy that apparently no one has attempted to make a complete list of all known bugs (if Netscape has one, they are not releasing it). The reason is simple: trying to produce a definitive list of bugs, for versions 2.0, 2.0.1, and 2.0.2, running on Windows 3.1, Windows 95, Windows NT, the Macintosh, and each of the many flavors of Unix that are supported would be a huge undertaking. Documenting all the bugs in all the versions on all the platforms in detail would probably require a book longer than this one.

For that reason, this section does not attempt to be a definitive list of bugs in Navigator 2.0. Instead, the aim is to inform you of the most serious and most commonly encountered bugs so that you will know how to avoid them and how to work around them when you can't avoid them. In a heterogeneous environment like the Internet, users of your scripts will be running a variety of Navigator versions on a variety of platforms. In effect, a bug on any one popular platform is a bug on all platforms, since the affected code or object cannot be safely used. For that reason, the bugs listed here are not categorized by platform or version.

Note that with release 2.0.2, development stopped on version 2.0 of Navigator. Thus, the bugs listed here will remain in the installed base of Navigator 2.0 browsers.

After describing the commonly encountered bugs, this chapter ends with a short section on debugging techniques that you may find useful for your scripts.

Security Hobbles

The first possibility you should consider when you encounter a strange bug in a script is to check whether you are violating Navigator's security restrictions. Remember that in versions 2.0.1 and 2.0.2, a script cannot read any properties of a window if the contents of that window came from a different server (i.e., a different host or a different protocol running on the same host) than the script did. The implications of this one restriction are far-reaching and have many implications for referencing properties across windows or frames. In particular, if you see the "Window has no properties" or "access disallowed from scripts at url to documents at url" error messages, you've probably run up against this security hobble.

See Chapter 20, JavaScript Security, for a list of a few more security restrictions. These restrictions are inconvenient and annoying, but they aren't really bugs; just limitations in the capabilities of JavaScript. Many of these restrictions may be lifted when data tainting becomes the default security model in Navigator 4.0.

General Bugs

This section covers general bugs that don't apply to any one particular JavaScript object.

Printing and saving generated text

When you output text to a document using the Document.write() method, Navigator can display this text. Unfortunately, because of the way HTML parsing works in Navigator, text generated by JavaScript cannot be printed or saved to a file. There is no workaround, except to replace your client-side JavaScript with a server-side CGI script.

A bug related to the previous one is that when the web browser is resized, all JavaScript in the web page is re-interpreted. This bug is fixed in 3.0 along with the printing bug.

Another related Navigator (non-JavaScript) bug is that when Navigator prints forms, it does not print the contents of the form elements.

JavaScript and tables

In general, JavaScript and tables do not mix well in Navigator 2.0. If you can, simply avoid putting JavaScript code in web pages that contain tables. If you cannot avoid it, then don't put form elements within tables--the table algorithm parses table contents twice, causing contained form elements to be created twice, and what are supposed to be single form elements end up in arrays of elements. Also, do not try to use JavaScript to output a portion (one or a few cells) of a table. If you need to generate some of the table with JavaScript code, use JavaScript to generate the entire table. These table problems have been fixed (mostly) in 3.0.

Line length limit

JavaScript was designed not to impose arbitrary length restrictions on lines of code. Unfortunately, because of a bug in the HTML parser, JavaScript complains if any lines in your program are over 254 characters long. Usually, the only time this occurs is when you have a very long string, in which case the end of the string gets truncated, and JavaScript complains of an "Unterminated string literal." The workaround is to break up your long lines and to avoid long strings. If you must use long strings, break them up into chunks that are shorter than 254 characters and use + to concatenate them.

Script size limit

Because of the nature of the 16-bit architecture of Windows 3.1, there is a limit on the length of scripts that can be handled on this platform. Programmers have reported having problems on this platform when their scripts reach 20Kb to 40Kb in length. A solution is to break the script up into separate modules and load each module into a separate frame or window, and then (carefully!) make function calls between frames or windows. When a script gets this long, another solution you should seriously consider is converting it to a CGI script run on the server, instead of forcing the user to download all the code.

Conversion of floating-point values to strings

The code used by JavaScript to convert floating-point values to strings is buggy and you will often see floating-point values displayed with a lot of trailing 9s. For example, the following code:

i = .15
alert(i);
will usually display a dialog box containing a string like ".14999999999995" instead of the ".15" that you would expect. This is a particular problem when dealing with numeric values that represent money. A workaround is to multiply your value by 100, and use the Math.round() method to round the result to the nearest integer. If you divide by 100 at this point, you'll have the same problem of trailing 9s, so the only solution is to convert your value times 100 to a string, use the String.substring() method to extract the dollars digits and cents digits, and then print these strings out, adding your own decimal point.

Date and time bugs

In Navigator 2.0, the Date object has quite a few bugs and is almost unusable. On Macintosh platforms, the time returned is off by an hour, and on all platforms, time zones are not handled well. Also, prior to version 2.0.2, there was a Navigator bug (not directly a JavaScript bug) in the handling daylight savings time. A side effect of this is that Navigator 2.0 and 2.0.1 cannot correctly determine whether a document on a server is newer than the cached version and so the Reload button does not always work correctly.

You can usually use the Date object to print out the current date, and you can use it to compute the interval (in milliseconds) between two dates or times in the same time zone, but you should probably not attempt more sophisticated uses of it than that.

lastIndexOf()

The String method lastIndexOf() should search a string backward starting from the specified character position within the string (0 for the first character, and string.length - 1 for the last character). In 2.0, however, it begins the search one character before the specified character. The workaround in 2.0 is to add 1 to the desired index.

eval()

Using the eval() function crashes Navigator 2.0 and 2.01 when running on Windows 3.1 platforms. This bug is fixed in 2.02, however. The workaround is to avoid eval(), or to use the Navigator object to check what platform the script is running on, and refuse to run on a Windows 3.1/Navigator 2.0 or 2.01 platform.

Window and Frame Bugs

The bugs described below affect the Window object and related areas of JavaScript. Some of them are suprisingly subtle, and because the Window object is so important in client-side JavaScript, these bugs may have wide reaching impact.

Window.open() method

The Window.open() method takes three arguments, a URL to display in the window, a window name, and a list of browser features that should be present or absent in the new window. Unfortunately, there are bugs with the first and third arguments.

On the Macintosh and some Unix platforms, the URL specified as the first argument to Window.open() is ignored. A commonly proposed workaround is to call open() a second time with the same URL specified. Another workaround is to set the location.href property of the window after it is created. For example, the second block of JavaScript code should be used instead of the first block:

// problems on Mac and Unix
var w = open("http://www.ora.com");
// following works on all platforms
var w = open("");
w.location.href = "http://www.ora.com";
      

In addition, the list of window features specified by the third argument to Window.open() does not work on Unix platforms running the X Window System. Width and height may be specified with this third argument, but no other features may be specified--all windows will be created without a menubar, toolbar, status line, and so on.

Dangling references

As discussed in Chapter 11, Windows and the JavaScript Name Space, the JavaScript memory management model is inadequate in Navigator 2.0. Because all objects allocated by a window are freed when the window unloads, references to those objects from other windows can be left dangling if the user closes the window or unexpectedly points the browser to a new page. If you attempt to use one of these references to a no-longer-existing object, you may get a corrupt value, or you may actually crash the browser.

It is debatable whether this is a bug or just an unfortunate misfeature of the JavaScript architecture in Navigator 2.0. In any case, the solution is to be very careful with your cross-window references.

Frame properties overwrite others

This is a bug that occurs only in a very specific situation, but it is bizarre and puzzling when you encounter it for the first time. When a window contains named frames, the references to those frames are stored in properties of the window. JavaScript apparently allocates the first few property "slots" of the window object for these frames. If you create other properties of the Window object before the frames are created, and if the window is a newly created one, then these properties may take up those first property "slots." Later, when the frame references are stored in those slots, the value of your properties will be overwritten.

This situation occurs only in a couple of specific cases. The first is when you have a <SCRIPT> tag that sets properties before a <FRAMESET> tag that defines frames. (Doing this is probably a poor programming practice, by the way.) The second is when you have a script that sets properties in a window and then generates the frames itself by explicitly outputting the necessary <FRAMESET> and <FRAME> tags.

A related bug that serves to make this bug even more mysterious is that frame properties of a Window object are not detected by a for/in loop until they have actually been used once by a script!

onLoad() event handler called early

When a document that does not contain frames but does contain images is loaded into a window, the Window object's onLoad() event handler may be called before the document is actually completely loaded. In this case, you cannot rely on onLoad() to tell you when the document is fully loaded and all document objects are defined. Therefore, you should be sure to check that the elements you want to access really exist before attempting to use them. For example, you might check that the last element of the last form is created before doing any manipulation of forms. If the element is not created when you check it, you can use setTimeout() to defer the code to be executed and to check again later.

Dialogs in onUnload()

Invoking the alert(), confirm(), or prompt() dialogs from an onUnload() event handler may crash Navigator. The only workaround is to avoid the temptation to do this--don't try to pop up a dialog to say good-bye to the user when they leave your page!

Scripts in framesets

Scripts that appear after a <FRAMESET> tag in a document will not be executed. This is not actually a bug, but a fact of the JavaScript architecture. Scripts may appear in the <HEAD> or <BODY> of a document. An HTML file that defines a frameset has a head--that portion that appears before the frameset--but does not have a body; the frameset is a substitute for the document body, and JavaScript rules do not allow scripts within frameset definitions.

JavaScript does allow scripts before the beginning of a frameset, but unless you have a good reason to do this, it probably isn't a good idea.

Status and defaultStatus

When you query the value of the status property of a Window, you get the value of the defaultStatus property of that Window, even if there is a status message currently displayed by the browser.

Also, on some platforms the defaultStatus message is not properly restored after a status message is displayed. For example, if you set the status property to a special message from the onMouseOver() event handler of a hypertext link, then this message may not be erased when the user moves the mouse off the link. You can address this problem by using setTimeout() to register a function to be executed after a couple of seconds which will explicitly set the status property to be the same as the defaultStatus.

setTimeout() memory leak

As discussed in Chapter 12, Programming with Windows, Navigator 2.0 does not reclaim any memory used by a page until that page unloads. The setTimeout() method allocates memory each time it is called, even when called repeatedly with the same string argument. Therefore, pages that perform repetitive actions (such as animation) with setTimeout() will allocate more and more memory, and may eventually crash the browser.

Document Object Bugs

These bugs affect the Document object.

Document background color

You can set the Document.bgColor property at any time to change the background color of a document. Unfortunately, on Unix/X11 platforms, and possibly some others, doing this also erases any text displayed in the window. If you really want to change the document color, you will have to reload or rewrite the document contents, which will cause a noticeable flicker after the color changes.

Closing the current document

Calling Document.close() on a document that contains the currently running script may crash the browser. The solution is to not do this. Obviously, any time Navigator crashes, it is a bug. But just as obviously, closing a document that contains the code that is currently being executed is not a useful thing to do, and it is not clear what such an attempt should actually do.

Overwriting the current script

If you call Document.write() on the current document from an event handler or timeout, or call a function that calls Document.write() from an event handler or timeout, you will implicitly close the current document and open a new one to perform the write into. What this does is erase the contents of the document, including the currently executing function or event handler. At best you will get undefined results if you attempt to do this. Often, though, you will crash the browser.

The solution, of course, is to not do this. Note that you can safely overwrite the document of a separate frame or window.

Form Bugs

This section describes bugs that affect HTML forms and the elements they contain.

Images and form event handlers

A strange but very commonly encountered bug is the following: If a document contains images and forms, then all the <IMG> tags must have WIDTH and HEIGHT attributes, or the event handlers of the form may be ignored. Usually, adding these tags speeds document loading times, so it is a good idea to get in the habit of using them with all images.

An alternative workaround is to follow your forms with an empty pair of <SCRIPT> and </SCRIPT> tags.

Backward radio and checkbox arrays

When an HTML form contains more than one element with the same name, then those elements will be stored in an array by that name. This is commonly done for radio buttons and checkboxes. The elements are supposed to appear in the array in the same order that they appear in the HTML source. For obscure reasons, however, if the elements do not have event handlers defined, then they will be placed in these arrays backward. If some of the elements have event handlers and some do not, then they will be placed in the array in some chaotic order. The solution is to provide an event handler for each element, even if it is only a dummy handler like the following:

<INPUT TYPE="checkbox" NAME="opt" VALUE="case-sensitive" onClick="0">

Of course, the order the elements are placed in the array is only an issue if you want to read or write the properties of those elements from your JavaScript code. If the form will simply be submitted to a server, then you don't have to worry about this bug.

Form method property

The method property of a Form object specifies the technique used to submit the contents of a form to a server. This property should be a read/write property, but in Navigator 2.0, it is read-only and may be set only when the form is defined in HTML.

Mutable string values

In JavaScript, strings are immutable objects, which means that the characters within them may not be changed and that any operations on strings actually create new strings. Strings are assigned by reference, not by value. In general, when an object is assigned by reference, a change made to the object through one reference will be visible through all other references to the object. Because strings cannot be changed, however, you can have multiple references to a string object and not worry that the string value will change without your knowing it.

Unfortunately, however, the value property of the Text and Textarea objects is a mutable string in Navigator 2.0. Thus, if you assign the value property to a variable, and then you set (or the user types) new text into the Text or Textarea object, the string your variable refers to will change.

The way to prevent this behavior is to force the value property to be copied by value rather than by reference. You can do this by creating a new string object with the + operator. Add the empty string to the value property to create a new string that contains the same text as the value property:

var address = document.form1.address.value + "";


Previous Home Next
Known JavaScript Bugs in Internet Explorer 3.0 Book Index Differences between Navigator 2.0 and 3.0

HTML: The Definitive Guide CGI Programming JavaScript: The Definitive Guide Programming Perl WebMaster in a Nutshell