58
has used the Back button to move to a different history entry, get the state information
associated with that entry, and re-create the previous state of the application. One
approach uses a hidden
<iframe>
to save state information and create entries in the
browser’s history. In order to create a new history entry, you dynamically write a new
document into this hidden frame using the
open()
and
write()
methods of the Docu-
ment object (see §15.10.2). The document content should include whatever state in-
formation is required to re-create the application state. When the user clicks the Back
button, the content of the hidden frame will change. Before HTML5, no events are
generated to notify you of this change, however, so in order to detect that the user has
clicked Back you might use
setInterval()
(§14.1) to check the hidden frame two or
three times a second to see if it has changed.
In practice, developers who need this kind of pre-HTML5 history management usually
rely on a prebuilt solution. Many JavaScript frameworks include one. There is a history
plug-in for jQuery, for example, and standalone history management libraries are also
available. RSH (Really Simple History) is one popular example. You can find it at http:
//code.google.com/p/reallysimplehistory/. §22.2 explains how to do history management
with HTML5.
14.4 Browser and Screen Information
Scripts sometimes need to obtain information about the web browser in which they are
running or the desktop on which the browser appears. This section describes the
navi
gator
and
screen
properties of the Window object. Those properties refer to Navigator
and Screen objects, respectively, and these objects provide information that allows a
script to customize its behavior based on its environment.
14.4.1 The Navigator Object
The
navigator
property of a Window object refers to a Navigator object that contains
browser vendor and version number information. The Navigator object is named after
the early Navigator browser from Netscape, but it is also supported by all other brows-
ers. (IE also supports
clientInformation
as a vendor-neutral synonym for
navigator
.
Unfortunately, other browsers have not adopted this more sensibly named property.)
In the past, the Navigator object was commonly used by scripts to determine if they
were running in Internet Explorer or Netscape. This “browser-sniffing” approach is
problematic because it requires constant tweaking as new browsers and new versions
of existing browsers are introduced. Today, feature testing (see §13.4.3) is preferred:
rather than making assumptions about particular browser versions and their features,
you simply test for the feature (i.e., the method or property) you need.
Browser sniffing is sometimes still valuable, however, such as when you need to work
around a specific bug that exists in a specific version of a specific browser. The Navi-
gator object has four properties that provide information about the browser that is
running, and you can use these properties for browser sniffing:
346 | Chapter 14: The Window Object
67
appName
The full name of the web browser. In IE, this is “Microsoft Internet Explorer”. In
Firefox, this property is “Netscape”. For compatibility with existing browser-
sniffing code, other browsers often report the name “Netscape” as well.
appVersion
This property typically begins with a number and follows that with a detailed string
that contains browser vendor and version information. The number at the start of
this string is often 4.0 or 5.0 to indicate generic compatibility with fourth- and fifth-
generation browsers. There is no standard format for the
appVersion
string, so
parsing it in a browser-independent way isn’t possible.
userAgent
The string that the browser sends in its
USER-AGENT
HTTP header. This property
typically contains all the information in
appVersion
and may contain additional
details as well. Like
appVersion
, there is no standard format. Since this property
contains the most information, browser-sniffing code typically uses it.
platform
A string that identifies the operating system (and possibly the hardware) on which
the browser is running.
The complexity of the Navigator properties demonstrates the futility of the browser-
sniffing approach to client-side compatibility. In the early days of the Web, lots of
browser-specific code was written that tested properties like
navigator.appName
. As new
browsers were written, vendors discovered that in order to correctly display existing
websites, they had to set the
appName
property to “Netscape”. A similar process caused
the number at the start of the
appVersion
to lose meaning, and today browser-sniffing
code must rely on the
navigator.userAgent
string and is more complicated than it once
was. Example 14-3 shows how to use regular expressions (from jQuery) to extract the
browser name and version number from
navigator.userAgent
.
Example 14-3. Browser sniffing using navigator.userAgent
// Define browser.name and browser.version for client sniffing, using code
// derived from jQuery 1.4.1. Both the name and number are strings, and both
// may differ from the public browser name and version. Detected names are:
//
// "webkit": Safari or Chrome; version is WebKit build number
// "opera": the Opera browser; version is the public version number
// "mozilla": Firefox or other gecko-based browsers; version is Gecko version
// "msie": IE; version is public version number
//
// Firefox 3.6, for example, returns: { name: "mozilla", version: "1.9.2" }.
var browser = (function() {
var s = navigator.userAgent.toLowerCase();
var match = /(webkit)[ \/]([\w.]+)/.exec(s) ||
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(s) ||
/(msie) ([\w.]+)/.exec(s) ||
!/compatible/.test(s) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(s) ||
[];
14.4 Browser and Screen Information | 347
Client-Side
JavaScript
Download from Wow! eBook <www.wowebook.com>
64
return { name: match[1] || "", version: match[2] || "0" };
}());
In addition to its browser vendor and version information properties, the Navigator
object has some miscellaneous properties and methods. The standardized and widely
implemented nonstandard properties include:
onLine
The
navigator.onLine
property (if it exists) specifies whether the browser is cur-
rently connected to the network. Applications may want to save state locally (using
techniques from Chapter 20) while they are offline.
geolocation
A Geolocation object that defines an API for determining the user’s geographical
location. See §22.1 for details.
javaEnabled()
A nonstandard method that should return
true
if the browser can run Java applets.
cookiesEnabled()
A nonstandard method that should return
true
if the browser can store persistent
cookies. May not return the correct value if cookies are configured on a site-by-site
basis.
14.4.2 The Screen Object
The
screen
property of a Window object refers to a Screen object that provides infor-
mation about the size of the user’s display and the number of colors available on it.
The
width
and
height
properties specify the size of the display in pixels. The
avail
Width
and
availHeight
properties specify the display size that is actually available; they
exclude the space required by features such as a desktop taskbar. The
colorDepth
prop-
erty specifies the bits-per-pixel value of the screen. Typical values are 16, 24, and 32.
The
window.screen
property and the Screen object to which it refers are both nonstan-
dard but widely implemented. You might use the Screen object to determine whether
your web app is running in a small form factor device such as a netbook computer. If
screen space is limited, you might choose to use smaller fonts and images, for example.
14.5 Dialog Boxes
The Window object provides three methods for displaying simple dialog boxes to the
user.
alert()
displays a message to the user and waits for the user to dismiss the dialog.
confirm()
displays a message, waits for the user to click an OK or Cancel button and
returns a boolean value. And
prompt()
displays a message, waits for the user to enter a
string, and returns that string. The following code uses all three methods:
do {
var name = prompt("What is your name?"); // Get a string
var correct = confirm("You entered '" + name + "'.\n" + // Get a boolean
348 | Chapter 14: The Window Object
93
"Click Okay to proceed or Cancel to re-enter.");
} while(!correct)
alert("Hello, " + name); // Display a plain message
Although the
alert()
,
confirm()
, and
prompt()
methods are very easy to use, good
design dictates that you use them sparingly, if at all. Dialog boxes like these are not a
common feature on the Web, and most users will find the dialog boxes produced by
these methods disruptive to their browsing experience. The only common use for these
methods today is debugging: JavaScript programmers sometimes insert
alert()
meth-
ods in code that is not working in an attempt to diagnose the problem.
Note that the messages displayed by
alert()
,
confirm()
, and
prompt()
are plain text,
not HTML-formatted text. You can format these dialog boxes only with spaces, new-
lines, and punctuation characters.
The
confirm()
and
prompt()
methods block—that is, these methods do not return until
the user dismisses the dialog boxes they display. This means that when you pop up one
of these boxes, your code stops running, and the currently loading document, if any,
stops loading until the user responds with the requested input. In most browsers, the
alert()
method also blocks and waits for the user to dismiss the dialog box, but this
is not required. For complete details on these methods, see
Window.alert
,
Window.con
firm
, and
Window.prompt
in Part IV.
In addition to the Window methods
alert()
,
confirm()
, and
prompt()
, a more com-
plicated method,
showModalDialog()
, displays a modal dialog box containing HTML-
formatted content and allows arguments to be passed to, and a value returned from,
the dialog.
showModalDialog()
displays a modal dialog in a browser window of its own.
The first argument is the URL that specifies the HTML content of the dialog box. The
second argument is an arbitrary value (arrays and objects are allowed) that will be made
available to scripts in the dialog as the value of the
window.dialogArguments
property.
The third argument is a nonstandard list of semicolon-separated name=value pairs that,
if supported, may configure the size or other attributes of the dialog. Use “dialogwidth”
and “dialogheight” to set the size of the dialog window, and use “resizable=yes” to
allow the user to resize the window.
The window displayed by this method is modal, and the call to
showModalDialog()
does
not return until the window is closed. When the window closes, the value of the
window.returnValue
property becomes the return value of the method call. The HTML
content of the dialog must typically include an OK button that sets
returnValue
, if
desired, and calls
window.close()
(see §14.8.1.1).
Example 14-4 is an HTML file suitable for use with
showModalDialog()
. The comment
at the top of the code includes a sample invocation of
showModalDialog()
, and Fig-
ure 14-1 shows the dialog created by the sample call. Note that most of the text that
appears in the dialog comes from the second argument to
showModalDialog()
, rather
than being hard-coded in the HTML.
14.5 Dialog Boxes | 349
Client-Side
JavaScript
38
Example 14-4. An HTML file for use with showModalDialog()
<!--
This is not a stand-alone HTML file. It must be invoked by showModalDialog().
It expects window.dialogArguments to be an array of strings.
The first element of the array is displayed at the top of the dialog.
Each remaining element is a label for a single-line text input field.
Returns an array of input field values when the user clicks Okay.
Use this file with code like this:
var p = showModalDialog("multiprompt.html",
["Enter 3D point coordinates", "x", "y", "z"],
"dialogwidth:400; dialogheight:300; resizable:yes");
-->
<form>
<fieldset id="fields"></fieldset> <!-- Dialog body filled in by script below -->
<div style="text-align:center"> <!-- Buttons to dismiss the dialog -->
<button onclick="okay()">Okay</button> <!-- Set return value and close -->
<button onclick="cancel()">Cancel</button> <!-- Close with no return value -->
</div>
<script>
// Create the HTML for the dialog body and display it in the fieldset
var args = dialogArguments;
var text = "<legend>" + args[0] + "</legend>";
for(var i = 1; i < args.length; i++)
text += "<label>" + args[i] + ": <input id='f" + i + "'></label><br>";
document.getElementById("fields").innerHTML = text;
// Close the dialog without setting a return value
function cancel() { window.close(); }
// Read the input field values and set a return value, then close
function okay() {
window.returnValue = []; // Return an array
for(var i = 1; i < args.length; i++) // Set elements from input fields
window.returnValue[i-1] = document.getElementById("f" + i).value;
window.close(); // Close the dialog. This makes showModalDialog() return.
}
</script>
</form>
Figure 14-1. An HTML dialog displayed with showModalDialog()
350 | Chapter 14: The Window Object
68
14.6 Error Handling
The
onerror
property of a Window object is an event handler that is invoked when an
uncaught exception propagates all the way up the call stack and an error message is
about to be displayed in the browser’s JavaScript console. If you assign a function to
this property, the function is invoked whenever a JavaScript error occurs in that win-
dow: the function you assign becomes an error handler for the window.
For historical reasons, the
onerror
event handler of the Window object is invoked with
three string arguments rather than with the one event object that is normally passed.
(Other client-side objects have
onerror
handlers to handle different error conditions,
but these are all regular event handlers that are passed a single event object.) The first
argument to
window.onerror
is a message describing the error. The second argument is
a string that contains the URL of the JavaScript code that caused the error. The third
argument is the line number within the document where the error occurred.
In addition to those three arguments, the return value of the
onerror
handler is signif-
icant. If the
onerror
handler returns
false
, it tells the browser that the handler has
handled the error and that no further action is necessary—in other words, the browser
should not display its own error message. Unfortunately, for historical reasons, an error
handler in Firefox must return
true
to indicate that it has handled the error.
The
onerror
handler is a holdover from the early days of JavaScript, when the core
language did not include the
try/catch
exception handling statement. It is rarely used
in modern code. During development, however, you might define an error handler like
this to explicitly notify you when an error occurs:
// Display error messages in a dialog box, but never more than 3
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "\n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;
14.7 Document Elements As Window Properties
If you name an element in your HTML document using the
id
attribute, and if the
Window object does not already have a property by that name, the Window object is
given a nonenumerable property whose name is the value of the
id
attribute and whose
name is the HTMLElement object that represents that document element.
As we’ve already noted, the Window object serves as the global object at the top of the
scope chain in client-side JavaScript, so this means that the
id
attributes you use in your
HTML documents become global variables accessible to your scripts. If your document
14.7 Document Elements As Window Properties | 351
Client-Side
JavaScript
90
includes the element
<button id="okay"/>
, you can refer to that element using the global
variable
okay
.
There is an important caveat, however: this doesn’t happen if the Window object al-
ready has a property by that name. Elements with the ids “history”, “location,” or
“navigator”, for example, won’t appear as global variables, because those IDs are al-
ready in use. Similarly, if your HTML document includes an element whose
id
is “x”
and you also declare and assign a value to the global variable
x
in your code, the ex-
plicitly declared variable will hide the implicit element variable. If the variable is de-
clared in a script that appears before the named element, its existence will prevent the
element from getting a window property of its own. And if the variable is declared in
a script that appears after the named element, your explicit assignment to the variable
overwrites the implicit value of the property.
In §15.2, you’ll learn that you can look up document elements by the value of their
HTML
id
attribute using the
document.getElementById()
method. Consider this
example:
var ui = ["input","prompt","heading"]; // An array of element ids
ui.forEach(function(id) { // For each id look up the element
ui[id] = document.getElementById(id); // and store it in a property
});
After running this code,
ui.input
,
ui.prompt
, and
ui.heading
refer to document ele-
ments. A script could use the global variables
input
and
heading
instead of
ui.input
and
ui.heading
. But recall from §14.5 that the Window object has a method named
prompt()
, so a script cannot use the global variable
prompt
instead of
ui.prompt
.
The implicit use of element IDs as global variables is a historical quirk of web browser
evolution. It is required for backward compatibility with existing web pages, but its use
is not recommended—any time a browser vendor defines a new property of the Win-
dow object it breaks any code that uses an implicit definition of that property name.
Instead, use
document.getElementById()
to look up elements explicitly. The use of this
method seems less onerous if we give it a simpler name:
var $ = function(id) { return document.getElementById(id); };
ui.prompt = $("prompt");
Many client-side libraries define a
$
function that looks up elements by ID like this.
(We’ll see in Chapter 19 that jQuery’s
$
function is a general-purpose element selection
method that returns one or more elements based on their ID, tag name,
class
attribute,
or other criteria.)
Any HTML element with an
id
attribute will become the value of a global variable,
assuming the ID is not already used by the Window object. The following HTML
elements also behave this way when given a
name
attribute:
<a> <applet> <area> <embed> <form> <frame> <frameset> <iframe> <img> <object>
The
id
element is required to be unique within a document: two elements cannot have
the same
id
. This is not true for the
name
attribute, however. If more than one of the
352 | Chapter 14: The Window Object
Documents you may be interested
Documents you may be interested