Chapter 8

The Browser Object Model

What’s in This Chapter?

  • Understanding the window object, the core of the BOM
  • Controlling windows, frames, and pop-ups
  • Page information from the location object
  • Using the navigator object to learn about the browser

Though ECMAScript describes it as the core of JavaScript, the Browser Object Model (BOM) is really the core of using JavaScript on the Web. The BOM provides objects that expose browser functionality independent of any web page content. For years, a lack of any real specification made the BOM both interesting and problematic, because browser vendors were free to augment it as they saw fit. The commonalities between browsers became de facto standards that have survived browser development mostly for the purpose of interoperability. Part of the HTML5 specification now covers the major aspects of the BOM, as the W3C seeks to standardize one of the most fundamental parts of JavaScript in the browser.

The window Object

At the core of the BOM is the window object, which represents an instance of the browser. The window object serves a dual purpose in browsers, acting as the JavaScript interface to the browser window and the ECMAScript Global object. This means that every object, variable, and function defined in a web page uses window as its Global object and has access to methods like parseInt().

The Global Scope

Since the window object doubles as the ECMAScript Global object, all variables and functions declared globally become properties and methods of the window object. Consider this example:

var age = 29;
function sayAge(){
alert(this.age);
}
alert(window.age); //29
sayAge(); //29
window.sayAge(); //29

Here, a variable named age and a function named sayAge() are defined in the global scope, which automatically places them on the window object. Thus, the variable age is also accessible as window.age, and the function sayAge() is also accessible via window.sayAge(). Since sayAge() exists in the global scope, this.age maps to window.age, and the correct result is displayed.

Despite global variables becoming properties of the window object, there is a slight difference between defining a global variable and defining a property directly on window: global variables cannot be removed using the delete operator, while properties defined directly on window can. For example:

image
var age = 29;
window.color = "red";
//throws an error in IE < 9, returns false in all other browsers
delete window.age;
//throws an error in IE < 9, returns true in all other browsers
delete window.color; //returns true
alert(window.age); //29
alert(window.color); //undefined

DeleteOperatorExample01.htm

Properties of window that were added via var statements have their [[Configurable]] attribute set to false and so may not be removed via the delete operator. Internet Explorer 8 and earlier enforced this by throwing an error when the delete operator is used on window properties regardless of how they were originally created. Internet Explorer 9 and later do not throw an error.

Another thing to keep in mind: attempting to access an undeclared variable throws an error, but it is possible to check for the existence of a potentially undeclared variable by looking on the window object. For example:

//this throws an error because oldValue is undeclared
var newValue = oldValue;
//this doesn't throw an error, because it's a property lookup
//newValue is set to undefined
var newValue = window.oldValue;

Keeping this in mind, there are many objects in JavaScript that are considered to be global, such as location and navigator (both discussed later in the chapter), but are actually properties of the window object.

image

Internet Explorer for Windows Mobile doesn’t allow direct creation of new properties or methods on the window object via window.property = value. All variables and functions declared globally, however, will still become members of window.

Window Relationships and Frames

If a page contains frames, each frame has its own window object and is stored in the frames collection. Within the frames collection, the window objects are indexed both by number (starting at 0, going from left to right, and then row by row) and by the name of the frame. Each window object has a name property containing the name of the frame. Consider the following:

image
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset rows="160,*">
<frame src="frame.htm" name="topFrame">
<frameset cols="50%,50%">
<frame src="anotherframe.htm" name="leftFrame">
<frame src="yetanotherframe.htm" name="rightFrame">
</frameset>
</frameset>
</html>

FramesetExample01.htm

This code creates a frameset with one frame across the top and two frames underneath. Here, the top frame can be referenced by window.frames[0] or window.frames["topFrame"]; however, you would probably use the top object instead of window to refer to these frames (making it top.frames[0], for instance).

The top object always points to the very top (outermost) frame, which is the browser window itself. This ensures that you are pointing to the correct frame from which to access the others. Any code written within a frame that references the window object is pointing to that frame’s unique instance rather than the topmost one. Figure 8-1 indicates the various ways that the frames in the previous example may be accessed from code that exists in the topmost window.

Another window object is called parent. The parent object always points to the current frame’s immediate parent frame. In some cases, parent may be equal to top, and when there are no frames, parent is equal to top (and both are equal to window). Consider the following example:

image
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset rows="100,*">
<frame src="frame.htm" name="topFrame">
<frameset cols="50%,50%">
<frame src="anotherframe.htm" name="leftFrame">
<frame src="anotherframeset.htm" name="rightFrame">
</frameset>
</frameset>
</html>

frameset1.htm

This frameset has a frame that contains another frameset, the code for which is as follows:

<html>
<head>
<title>Frameset Example</title>
</head>
<frameset cols="50%,50%">
<frame src="red.htm" name="redFrame">
<frame src="blue.htm" name="blueFrame">
</frameset>
</html>

anotherframeset.htm

When the first frameset is loaded into the browser, it loads another frameset into rightFrame. If code is written inside redFrame (or blueFrame), the parent object points to rightFrame. If, however, the code is written in topFrame, then parent points to top because its immediate parent is the outermost frame. Figure 8-2 shows the values of the various window objects when this example is loaded into a web browser.

Note that the topmost window will never have a value set for name unless the window was opened using window.open(), as discussed later in this chapter.

There is one final window object, called self, which always points to window. The two can, in fact, be used interchangeably. Even though it has no separate value, self is included for consistency with the top and parent objects.

Each of these objects is actually a property of the window object, accessible via window.parent, window.top, and so on. This means it’s possible to chain window objects together, such as window.parent.parent.frames[0].

image

Whenever frames are used, multiple Global objects exist in the browser. Global variables defined in each frame are defined to be properties of that frame’s window object. Since each window object contains the native type constructors, each frame has its own version of the constructors, which are not equal. For example, top.Object is not equal to top.frames[0].Object, which affects the use of instanceof when objects are passed across frames.

Window Position

The position of a window object may be determined and changed using various properties and methods. Internet Explorer, Safari, Opera, and Chrome all provide screenLeft and screenTop properties that indicate the window’s location in relation to the left and top of the screen, respectively. Firefox provides this functionality through the screenX and screenY properties, which are also supported in Safari and Chrome. Opera supports screenX and screenY, but you should avoid using them in Opera, because they don’t correspond to screenLeft and screenTop. The following code determines the left and top positions of the window across browsers:

image
var leftPos = (typeof window.screenLeft == "number") ?
window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ?
window.screenTop : window.screenY;

WindowPositionExample01.htm

This example uses the ternary operator to determine if the screenLeft and screenTop properties exist. If they do (which is the case in Internet Explorer, Safari, Opera, and Chrome), they are used. If they don’t exist (as in Firefox), screenX and screenY are used.

There are some quirks to using these values. In Internet Explorer, Opera, and Chrome, screenLeft and screenTop refer to the space from the left and top of the screen to the page view area represented by window. If the window object is the topmost object and the browser window is at the very top of the screen (with a y-coordinate of 0), the screenTop value will be the pixel height of the browser toolbars that appear above the page view area. Firefox and Safari treat these coordinates as being related to the entire browser window, so placing the window at y-coordinate 0 on the screen returns a top position of 0.

To further confuse things, Firefox, Safari, and Chrome always return the values of top.screenX and top.screenY for every frame on the page. Even if a page is offset by some margins, these same values are returned every time screenX and screenY are used in relation to a window object. Internet Explorer and Opera give accurate coordinates for the location of frames in relation to the screen edges.

The end result is that you cannot accurately determine the left and top coordinates of a browser window across all browsers. It is possible, however, to accurately move the window to a new position using the moveTo() and moveBy() methods. Each method accepts two arguments. moveTo() expects the x and y coordinates to move to, and moveBy() expects the number of pixels to move in each direction. Consider this example:

//move the window to the upper-left coordinate
window.moveTo(0,0);
//move the window down by 100 pixels
window.moveBy(0, 100);
//move the window to position (200, 300)
window.moveTo(200, 300);
//move the window left by 50 pixels
window.moveBy(-50, 0);

These methods may be disabled by the browser and are disabled by default for the main browser window in Internet Explorer 7+, Chrome, and Opera. None of these methods work for frames; they apply only to the topmost window object.

Window Size

Determining the size of a window cross-browser is not straightforward. Internet Explorer 9+, Firefox, Safari, Opera, and Chrome all provide four properties: innerWidth, innerHeight, outerWidth, and outerHeight. In Internet Explorer 9+, Safari, Firefox, and Chrome, outerWidth and outerHeight return the dimensions of the browser window itself (regardless of whether it’s used on the topmost window object or on a frame). In Opera, these values are the size of the page viewport. The innerWidth and innerHeight properties indicate the size of the page viewport inside the browser window (minus borders and toolbars).

Internet Explorer 8 and earlier versions offer no way to get the current dimensions of the browser window; however, they do provide information about the viewable area of the page via the DOM.

The document.documentElement.clientWidth and document.documentElement.clientHeight properties provide the width and height of the page viewport in Internet Explorer, Firefox, Safari, Opera, and Chrome. In Internet Explorer 6, the browser must be in standards mode for these properties to be available; when in quirks mode, the information is available via document.body.clientWidth and document.body.clientHeight. When Chrome is in quirks mode, the values of clientWidth and clientHeight on document.documentElement and document.body both contain the viewport dimensions.

The end result is that there’s no accurate way to determine the size of the browser window itself, but it is possible to get the dimensions of the page viewport, as shown in the following example:

image
var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
if (document.compatMode == "CSS1Compat"){
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else {
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}

WindowSizeExample01.htm

In this code, pageWidth and pageHeight are assigned initial values of window.innerWidth and window.innerHeight, respectively. A check is then done to see if the value of pageWidth is a number; if not, then the code determines if the page is in standards mode by using document.compatMode. (This property is discussed fully in Chapter 11.) If it is, then document.documentElement.clientWidth and document.documentElement.clientHeight are used; otherwise, document.body.clientWidth and document.body.clientHeight are used.

For mobile devices, window.innerWidth and window.innerHeight are the dimensions of the visual viewport, which is the visible area of the page on the screen. Mobile Internet Explorer doesn’t support these properties but provides the same information on document.documentElement.clientWidth and document.documentElement.clientHeight. These values change as you zoom in or out of a page.

In other mobile browsers, the measurements of document.documentElement provide measurements for the layout viewport, which are the actual dimensions of the rendered page (as opposed to the visual viewport, which is only a small portion of the entire page). Mobile Internet Explorer stores this information in document.body.clientWidth and document.body.clientHeight. These values do not change as you zoom in and out.

Because of these differences from desktop browsers, you may need to first determine if the user is on a mobile device before deciding which measurements to use and honor.

image

The topic of mobile viewports is a complex one with various exceptions and caveats. Peter-Paul Koch, a mobile development consultant, outlined all of his research at http://quirksmode.org/mobile/viewports2.html. This is recommended reading if you’re developing for mobile devices.

The browser window can be resized using the resizeTo() and resizeBy() methods. Each method accepts two arguments: resizeTo() expects a new width and height, and resizeBy() expects the differences in each dimension. Here’s an example:

//resize to 100 x 100
window.resizeTo(100, 100);
//resize to 200 x 150
window.resizeBy(100, 50);
//resize to 300 x 300
window.resizeTo(300, 300);

As with the window-movement methods, the resize methods may be disabled by the browser and are disabled by default in Internet Explorer 7+, Chrome, and Opera. Also like the movement methods, these methods apply only to the topmost window object.

Navigating and Opening Windows

The window.open() method can be used both to navigate to a particular URL and to open a new browser window. This method accepts four arguments: the URL to load, the window target, a string of features, and a Boolean value indicating that the new page should take the place of the currently loaded page in the browser history. Typically only the first three arguments are used; the last argument applies only when not opening a new window.

If the second argument passed to window.open() is the name of a window or frame that already exists, then the URL is loaded into the window or frame with that name. Here’s an example:

//same as <a href="http://www.wrox.com" target="topFrame"></a>
window.open("http://www.wrox.com/", "topFrame");

This line of code acts as if the user clicked a link with the href attribute set to “http://www.wrox.com” and the target attribute set to "topFrame". If there is a window or frame named "topFrame", then the URL will be loaded there; otherwise, a new window is created and given the name "topFrame". The second argument may also be any of the special window names: _self, _parent, _top, or _blank.

Popping Up Windows

When the second argument doesn’t identify an existing window or frame, a new window or tab is created based on a string passed in as the third argument. If the third argument is missing, a new browser window (or tab, based on browser settings) is opened with all of the default browser window settings. (Toolbars, the location bar, and the status bar are all set based on the browser’s default settings.) The third argument is ignored when not opening a new window.

The third argument is a comma-delimited string of settings indicating display information for the new window. The following table describes the various options.

Setting Value(s) Description
fullscreen "yes" or "no" Indicates that the browser window should be maximized when created. Internet Explorer only.
height Number The initial height of the new window. This cannot be less than 100.
left Number The initial left coordinate of the new window. This cannot be a negative number.
location "yes" or "no" Indicates if the location bar should be displayed. The default varies based on the browser. When set to "no", the location bar may be either hidden or disabled (browser-dependent).
menubar "yes" or "no" Indicates if the menu bar should be displayed. The default is "no".
resizable "yes" or "no" Indicates if the new window can be resized by dragging its border. The default is "no".
scrollbars "yes" or "no" Indicates if the new window allows scrolling if the content cannot fit in the viewport. The default is "no".
status "yes" or "no" Indicates if the status bar should be displayed. The default varies based on the browser.
toolbar "yes" or "no" Indicates if the toolbar should be displayed. The default is "no".
top Number The initial top coordinate of the new window. This cannot be a negative number.
width Number The initial width of the new window. This cannot be less than 100.

Any or all of these settings may be specified as a comma-delimited set of name-value pairs. The name-value pairs are indicated by an equal sign. (No white space is allowed in the feature string.) Consider the following example:

window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");

This code opens a new resizable window that’s 400 3 400 and positioned 10 pixels from the top and left of the screen.

The window.open() method returns a reference to the newly created window. This object is the same as any other window object except that you typically have more control over it. For instance, some browsers that don’t allow you to resize or move the main browser window by default may allow you to resize or move windows that you’ve created using window.open(). This object can be used to manipulate the newly opened window in the same way as any other window, as shown in this example:

var wroxWin =window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
//resize it
wroxWin.resizeTo(500, 500);
//move it
wroxWin.moveTo(100, 100);

It’s possible to close the newly opened window by calling the close() method as follows:

wroxWin.close();

This method works only for pop-up windows created by window.open(). It’s not possible to close the main browser window without confirmation from the user. It is possible, however, for the pop-up window to close itself without user confirmation by calling top.close(). Once the window has been closed, the window reference still exists but cannot be used other than to check the closed property, as shown here:

wroxWin.close();
alert(wroxWin.closed); //true

The newly created window object has a reference back to the window that opened it via the opener property. This property is defined only on the topmost window object (top) of the pop-up window and is a pointer to the window or frame that called window.open(). For example:

var wroxWin =window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
alert(wroxWin.opener == window); //true

Even though there is a pointer from the pop-up window back to the window that opened it, there is no reverse relationship. Windows do not keep track of the pop-ups that they spawn, so it’s up to you to keep track if necessary.

Some browsers, such as Internet Explorer 8+ and Google Chrome, try to run each tab in the browser as a separate process. When one tab opens another, the window objects need to be able to communicate with one another, so the tabs cannot run in separate processes. Chrome allows you to indicate that the newly created tab should be run in a separate process by setting the opener property to null, as in the following example:

var wroxWin =window.open("http://www.wrox.com/","wroxWindow",
"height=400,width=400,top=10,left=10,resizable=yes");
wroxWin.opener = null;

Setting opener to null indicates to the browser that the newly created tab doesn’t need to communicate with the tab that opened it, so it may be run in a separate process. Once this connection has been severed, there is no way to recover it.

Security Restrictions

Pop-up windows went through a period of overuse by advertisers online. Pop-ups were often disguised as system dialogs to get the user to click on an advertisement. Since these pop-up web pages were styled to look like system dialogs, it was unclear to the user whether the dialog was legitimate. To aid in this determination, browsers began putting limits on the configuration of pop-up windows.

Internet Explorer 6 on Windows XP Service Pack 2 implemented multiple security features on pop-up windows, including not allowing pop-up windows to be created or moved offscreen and ensuring that the status bar cannot be turned off. Beginning with Internet Explorer 7, the location bar cannot be turned off and pop-up windows can’t be moved or resized by default. Firefox 1 turned off the ability to suppress the status bar, so all pop-up windows have to display the status bar regardless of the feature string passed into window.open(). Firefox 3 forces the location bar to always be displayed on pop-up windows. Opera opens pop-up windows only within its main browser window but doesn’t allow them to exist where they might be confused with system dialogs.

Additionally, browsers will allow the creation of pop-up windows only after a user action. A call to window.open() while a page is still being loaded, for instance, will not be executed and may cause an error to be displayed to the user. Pop-up windows may be opened based only on a click or a key press.

Chrome uses a different approach to handling pop-up windows that aren’t initiated by the user. Instead of blocking them, the browser displays only the title bar of the pop-up window and places it in the lower-right corner of the browser window.

image

Internet Explorer lifts some restrictions on pop-up windows when displaying a web page stored on the computer’s hard drive. The same code, when run on a server, will invoke the pop-up restrictions.

Pop-up Blockers

Most browsers have pop-up–blocking software built in, and for those that don’t, utilities such as the Yahoo! Toolbar have built-in pop-up blockers. The result is that most unexpected pop-ups are blocked. When a pop-up is blocked, one of two things happens. If the browser’s built-in pop-up blocker stopped the pop-up, then window.open() will most likely return null. In that case, you can tell if a pop-up was blocked by checking the return value, as shown in the following example:

var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
alert("The popup was blocked!");
}

When a browser add-on or other program blocks a pop-up, window.open() typically throws an error. So to accurately detect when a pop-up has been blocked, you must check the return value and wrap the call to window.open() in a try-catch block, as in this example:

image
var blocked = false;
try {
var wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
blocked = true;
}
} catch (ex){
blocked = true;
}
if (blocked){
alert("The popup was blocked!");
}

PopupBlockerExample01.htm

This code accurately detects if a pop-up blocker has blocked the call to window.open(), regardless of the method being used. Note that detecting if a pop-up was blocked does not stop the browser from displaying its own message about a pop-up being blocked.

Intervals and Timeouts

JavaScript execution in a browser is single-threaded, but does allow for the scheduling of code to run at specific points in time through the use of timeouts and intervals. Timeouts execute some code after a specified amount of time, whereas intervals execute code repeatedly, waiting a specific amount of time in between each execution.

You set a timeout using the window’s setTimeout() method, which accepts two arguments: the code to execute and the number of time (in milliseconds) to wait before attempting to execute the code. The first argument can be either a string containing JavaScript code (as would be used with eval()) or a function. For example, both of the following display an alert after 1 second:

image
//avoid!
setTimeout("alert('Hello world!') ", 1000);
//preferred
setTimeout(function() {
alert("Hello world!");
}, 1000);

TimeoutExample01.htm

Even though both of these statements work, it’s considered poor practice to use a string as the first argument, because it brings with it performance penalties.

The second argument, the number of milliseconds to wait, is not necessarily when the specified code will execute. JavaScript is single-threaded and, as such, can execute only one piece of code at a time. To manage execution, there is a queue of JavaScript tasks to execute. The tasks are executed in the order in which they were added to the queue. The second argument of setTimeout() tells the JavaScript engine to add this task onto the queue after a set number of milliseconds. If the queue is empty, then that code is executed immediately; if the queue is not empty, the code must wait its turn.

When setTimeout() is called, it returns a numeric ID for the timeout. The timeout ID is a unique identifier for the scheduled code that can be used to cancel the timeout. To cancel a pending timeout, use the clearTimeout() method and pass in the timeout ID, as in the following example:

//set the timeout
var timeoutId = setTimeout(function() {
alert("Hello world!");
}, 1000);
//nevermind - cancel it
clearTimeout(timeoutId);

TimeoutExample02.htm

As long as clearTimeout() is called before the specified amount of time has passed, a timeout can be canceled completely. Calling clearTimeout() after the code has been executed has no effect.

image

All code executed by a timeout runs in the global scope, so the value of this inside the function will always point to window when running in nonstrict mode and undefined when running in strict mode.

Intervals work in the same way as timeouts except that they execute the code repeatedly at specific time intervals until the interval is canceled or the page is unloaded. The setInterval() method lets you set up intervals, and it accepts the same arguments as setTimeout(): the code to execute (string or function) and the milliseconds to wait between executions. Here’s an example:

image
//avoid!
setInterval("alert('Hello world!') ", 10000);
//preferred
setInterval(function() {
alert("Hello world!");
}, 10000);

IntervalExample01.htm

The setInterval() method also returns an interval ID that can be used to cancel the interval at some point in the future. The clearInterval() method can be used with this ID to cancel all pending intervals. This ability is more important for intervals than timeouts since, if left unchecked, they continue to execute until the page is unloaded. Here is a common example of interval usage:

var num = 0;
var max = 10;
var intervalId = null;
function incrementNumber() {
num++;
//if the max has been reached, cancel all pending executions
if (num == max) {
clearInterval(intervalId);
alert("Done");
}
}
intervalId = setInterval(incrementNumber, 500);

IntervalExample02.htm

In this example, the variable num is incremented every half second until it finally reaches the maximum number, at which point the interval is canceled. This pattern can also be implemented using timeouts, as shown here:

image
var num = 0;
var max = 10;
function incrementNumber() {
num++;
//if the max has not been reached, set another timeout
if (num < max) {
setTimeout(incrementNumber, 500);
} else {
alert("Done");
}
}
setTimeout(incrementNumber, 500);

TimeoutExample03.htm

Note that when you’re using timeouts, it is unnecessary to track the timeout ID, because the execution will stop on its own and continue only if another timeout is set. This pattern is considered a best practice for setting intervals without actually using intervals. True intervals are rarely used in production environments because the time between the end of one interval and the beginning of the next is not necessarily guaranteed, and some intervals may be skipped. Using timeouts, as in the preceding example, ensures that can’t happen. Generally speaking, it’s best to avoid intervals.

System Dialogs

The browser is capable of invoking system dialogs to display to the user through the alert(), confirm(), and prompt() methods. These dialogs are not related to the web page being displayed in the browser and do not contain HTML. Their appearance is determined by operating system and/or browser settings rather than CSS. Additionally, each of these dialogs is synchronous and modal, meaning code execution stops when a dialog is displayed, and resumes after it has been dismissed.

The alert() method has been used throughout this book. It simply accepts a string to display to the user. When alert() is called, a system message box displays the specified text to the user, followed by a single OK button. For example, alert("Hello world!") renders the dialog box shown in Figure 8-3 when used with Internet Explorer on Windows XP.

Alert dialogs are typically used when users must be made aware of something that they have no control over, such as an error. A user’s only choice is to dismiss the dialog after reading the message.

The second type of dialog is invoked by calling confirm(). A confirm dialog looks similar to an alert dialog in that it displays a message to the user. The main difference between the two is the presence of a Cancel button along with the OK button, which allows the user to indicate if a given action should be taken. For example, confirm("Are you sure?") displays the confirm dialog box shown in Figure 8-4.

To determine if the user clicked OK or Cancel, the confirm() method returns a Boolean value: true if OK was clicked, or false if Cancel was clicked or the dialog box was closed by clicking the X in the corner. Typical usage of a confirm dialog looks like this:

if (confirm("Are you sure?")) {
alert("I'm so glad you're sure! ");
} else {
alert("I'm sorry to hear you're not sure. ");
}

In this example, the confirm dialog is displayed to the user in the first line, which is a condition of the if statement. If the user clicks OK, an alert is displayed saying, “I'm so glad you're sure!” If, however, the Cancel button is clicked, an alert is displayed saying, “I'm sorry to hear you're not sure.” This type of pattern is often employed when the user tries to delete something, such as an e-mail message.

The final type of dialog is displayed by calling prompt(), which prompts the user for input. Along with OK and Cancel buttons, this dialog has a text box where the user may enter some data. The prompt() method accepts two arguments: the text to display to the user, and the default value for the text box (which can be an empty string). Calling prompt("What's your name?", "Michael") results in the dialog box shown in Figure 8-5.

If the OK button is clicked, prompt() returns the value in the text box; if Cancel is clicked or the dialog is otherwise closed without clicking OK, the function returns null. Here’s an example:

var result = prompt("What is your name? ", "");
if (result !== null) {
alert("Welcome, " + result);
}

These system dialogs can be helpful for displaying information to the user and asking for confirmation of decisions. Since they require no HTML, CSS, or JavaScript to be loaded, they are fast and easy ways to enhance a web application.

Chrome and Opera introduced a special feature regarding these system dialogs. If the actively running script produces two or more system dialogs during its execution, each subsequent dialog after the first displays a check box that allows the user to disable any further dialogs until the page reloads (see Figure 8-6).

When the check box is checked and the dialog box is dismissed, all further system dialogs (alerts, confirms, and prompts) are blocked until the page is reloaded. Chrome gives the developer no indication as to whether the dialog was displayed. The dialog counter resets whenever the browser is idle, so if two separate user actions produce an alert, the check box will not be displayed in either; if a single user action produces two alerts in a row, the second will contain the check box. Since being first introduced, this feature has made it into Internet Explorer 9 and Firefox 4.

Two other types of dialogs can be displayed from JavaScript: find and print. Both of these dialogs are displayed asynchronously, returning control to the script immediately. The dialogs are the same as the ones the browser employs when the user selects either Find or Print from the browser’s menu. These are displayed using the find() and print() methods on the window object as follows:

//display print dialog
window.print();
//display find dialog
window.find();

These two methods give no indication as to whether the user has done anything with the dialog, so it is difficult to make good use of them. Furthermore, since they are asynchronous, they don’t contribute to Chrome’s dialog counter and won’t be affected by the user opting to disallow further dialogs.

The location Object

One of the most useful BOM objects is location, which provides information about the document that is currently loaded in the window, as well as general navigation functionality. The location object is unique in that it is a property of both window and document; both window.location and document.location point to the same object. Not only does location know about the currently loaded document, but it also parses the URL into discrete segments that can be accessed via a series of properties. These properties are enumerated in the following table (the location prefix is assumed).

Property Name Example Description
hash "#contents" The URL hash (the pound sign followed by zero or more characters), or an empty string if the URL doesn’t have a hash.
host www.wrox.com:80 The name of the server and port number if present.
hostname www.wrox.com The name of the server without the port number.
href http://www.wrox.com The full URL of the currently loaded page. The toString() method of location returns this value.
pathname "/WileyCDA/" The directory and/or filename of the URL.
port "8080" The port of the request if specified in the URL. If a URL does not contain a port, then this property returns an empty string.
protocol "http:" The protocol used by the page. Typically "http:" or "https:".
search "?q=javascript" The query string of the URL. It returns a string beginning with a question mark.

Query String Arguments

Most of the information in location is easily accessible from these properties. The one part of the URL that isn’t provided is an easy-to-use query string. Though location.search returns everything from the question mark until the end of the URL, there is no immediate access to query-string arguments on a one-by-one basis. The following function parses the query string and returns an object with entries for each argument:

image
function getQueryStringArgs(){
//get query string without the initial ?
var qs = (location.search.length > 0 ? location.search.substring(1) : ""),
//object to hold data
args = {},
//get individual items
items = qs.length ? qs.split("&") : [],
item = null,
name = null,
value = null,
//used in for loop
i = 0,
len = items.length;
//assign each item onto the args object
for (i=0; i < len; i++){
item = items[i].split("=");
name = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if (name.length) {
args[name] = value;
}
}
return args;
}

LocationExample01.htm

The first step in this function is to strip off the question mark from the beginning of the query string. This happens only if location.search has one or more characters. The arguments will be stored on the args object, which is created using object-literal format. Next, the query string is split on the ampersand character, returning an array of strings in the format name=value. The for loop iterates over this array and then splits each item on the equal sign, returning an array where the first item is the name of the argument and the second item is the value. The name and value are each decoded using decodeURIComponent() (since query-string arguments are supposed to be encoded). Last, the name is assigned as a property on the args object and its value is set to value. This function is used as follows:

//assume query string of ?q=javascript&num=10
var args = getQueryStringArgs();
alert(args["q"]); //"javascript"
alert(args["num"]); //"10"

Each of the query-string arguments is now a property on the returned object, which provides fast access to each argument.

Manipulating the Location

The browser location can be changed in a number of ways using the location object. The first, and most common, way is to use the assign() method and pass in a URL, as in the following example:

location.assign("http://www.wrox.com");

This immediately starts the process of navigating to the new URL and makes an entry in the browser’s history stack. If location.href or window.location is set to a URL, the assign() method is called with the value. For example, both of the following perform the same behavior as calling assign() explicitly:

window.location = "http://www.wrox.com";
location.href = "http://www.wrox.com";

Of these three approaches to changing the browser location, setting location.href is most often seen in code.

Changing various properties on the location object can also modify the currently loaded page. The hash, search, hostname, pathname, and port properties can be set with new values that alter the current URL, as in this example:

//assume starting at http://www.wrox.com/WileyCDA/
//changes URL to "http://www.wrox.com/WileyCDA/#section1"
location.hash = "#section1";
//changes URL to "http://www.wrox.com/WileyCDA/?q=javascript"
location.search = "?q=javascript";
//changes URL to "http://www.yahoo.com/WileyCDA/"
location.hostname = "www.yahoo.com";
//changes URL to "http://www.yahoo.com/mydir/"
location.pathname = "mydir";
//changes URL to "http://www.yahoo.com:8080/WileyCDA/

Each time a property on location is changed, with the exception of hash, the page reloads with the new URL.

image

Changing the value of hash causes a new entry in the browser’s history to be recorded as of Internet Explorer 8+, Firefox, Safari 2+, Opera 9+, and Chrome. In earlier Internet Explorer versions, the hash property was updated not when Back or Forward was clicked but only when a link containing a hashed URL was clicked.

When the URL is changed using one of the previously mentioned approaches, an entry is made in the browser’s history stack so the user may click the Back button to navigate to the previous page. It is possible to disallow this behavior by using the replace() method. This method accepts a single argument, the URL to navigate to, but does not make an entry in the history stack. After calling replace(), the user cannot go back to the previous page. Consider this example:

image
<!DOCTYPE html>
<html>
<head>
<title>You won't be able to get back here</title>
</head>
<body>
<p>Enjoy this page for a second, because you won't be coming back here.</p>
<script type="text/javascript">
setTimeout(function () {
location.replace("http://www.wrox.com/");
}, 1000);
</script>
</body>
</html>

LocationReplaceExample01.htm

If this page is loaded into a web browser, it will redirect to www.wrox.com after a second. At that point, the Back button will be disabled, and you won’t be able to navigate back to this example page without typing in the complete URL again.

The last method of location is reload(), which reloads the currently displayed page. When reload() is called with no argument, the page is reloaded in the most efficient way possible, which is to say that the page may be reloaded from the browser cache if it hasn’t changed since the last request. To force a reload from the server, pass in true as an argument like this:

location.reload(); //reload - possibly from cache
location.reload(true); //reload - go back to the server

Any code located after a reload() call may or may not be executed, depending on factors such as network latency and system resources. For this reason, it is best to have reload() as the last line of code.

The navigator Object

Originally introduced in Netscape Navigator 2, the navigator object is the standard for browser identification on the client. Though some browsers offer alternate ways to provide the same or similar information (for example, window.clientInformation in Internet Explorer and window.opera in Opera), the navigator object is common among all JavaScript-enabled web browsers. As with other BOM objects, each browser supports its own set of properties. The following table lists each available property and method, along with which browser versions support it.

image

image

image

The navigator object’s properties are typically used to determine the type of browser that is running a web page (discussed fully in Chapter 9).

Detecting Plug-ins

One of the most common detection procedures is to determine whether the browser has a particular plug-in installed. For browsers other than Internet Explorer, this can be determined using the plugins array. Each item in the array contains the following properties:

  • name — The name of the plug-in
  • description — The description of the plug-in
  • filename — The filename for the plug-in
  • length — The number of MIME types handled by this plug-in

Typically, the name contains all of the information that’s necessary to identify a plug-in, though this is not an exact science. Plug-in detection is done by looping over the available plug-ins and comparing a plug-in’s name to a given name, as in this example:

image
//plugin detection - doesn't work in Internet Explorer
function hasPlugin(name){
name = name.toLowerCase();
for (var i=0; i < navigator.plugins.length; i++){
if (navigator.plugins[i].name.toLowerCase().indexOf(name) > -1){
return true;
}
}
return false;
}
//detect flash
alert(hasPlugin("Flash"));
//detect quicktime
alert(hasPlugin("QuickTime"));

PluginDetectionExample01.htm

The hasPlugin() example accepts a single argument: the name of a plug-in to detect. The first step is to convert that name to lowercase for easier comparison. Next, the plugins array is iterated over, and each name property is checked via indexOf() to see if the passed-in name appears somewhere in that string. This comparison is done in all lowercase to avoid casing errors. The argument should be as specific as possible to avoid confusion. Strings such as "Flash" and "QuickTime" are unique enough that there should be little confusion. This method works for detecting plug-ins in Firefox, Safari, Opera, and Chrome.

image

Each plugin object is also an array of MimeType objects that can be accessed using bracket notation. Each MimeType object has four properties: description, which is a description of the MIME type; enabledPlugin, which is a pointer back to the plugin object; suffixes, which is a comma-delimited string of file extensions for the MIME type; and type, which is the full MIME type string.

Detecting plug-ins in Internet Explorer is more problematic, because it doesn’t support Netscape-style plug-ins. The only way to detect plug-ins in Internet Explorer is to use the proprietary ActiveXObject type and attempt to instantiate a particular plug-in. Plug-ins are implemented in Internet Explorer using COM objects, which are identified by unique strings. So to check for a particular plug-in, you must know its COM identifier. For instance, the identifier for Flash is "ShockwaveFlash.ShockwaveFlash". With this information, you can write a function to determine if the plug-in is installed in Internet Explorer as follows:

image
//plugin detection for Internet Explorer
function hasIEPlugin(name){
try {
new ActiveXObject(name);
return true;
} catch (ex){
return false;
}
}
//detect flash
alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
//detect quicktime
alert(hasIEPlugin("QuickTime.QuickTime"));

PluginDetectionExample02.htm

In this example, the hasIEPlugin() function accepts a COM identifier as its sole argument. In the function, an attempt is made to create a new ActiveXObject instance. This is encapsulated in a try-catch statement because an attempt to create an unknown COM object will throw an error. Therefore, if the attempt is successful, the function returns true. If there is an error, the catch block gets executed, which returns false. This code then checks to see if the Flash and QuickTime plug-ins are available in Internet Explorer.

Since these two plug-in–detection methods are so different, it’s typical to create functions that test for specific plug-ins rather than use the generic methods described previously. Consider this example:

//detect flash for all browsers
function hasFlash(){
var result = hasPlugin("Flash");
if (!result){
result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
}
return result;
}
//detect quicktime for all browsers
function hasQuickTime(){
var result = hasPlugin("QuickTime");
if (!result){
result = hasIEPlugin("QuickTime.QuickTime");
}
return result;
}
//detect flash
alert(hasFlash());
//detect quicktime
alert(hasQuickTime());

PluginDetectionExample03.htm

This code defines two functions: hasFlash() and hasQuickTime(). Each function attempts to use the non–Internet Explorer plug-in–detection code first. If that method returns false (which it will for Internet Explorer), the Internet Explorer plug-in–detection method is called. If the Internet Explorer plug-in–detection method also returns false, then the result of the overall method is false. if either plug-in–detection function returns true, then the overall method returns true.

image

The plugins collection has a method called refresh(), which refreshes plugins to reflect any newly installed plug-ins. This method accepts a single argument: a Boolean value indicating if the page should be reloaded. When set to true, all pages containing plug-ins are reloaded; otherwise the plugins collection is updated, but the page is not reloaded.

Registering Handlers

Firefox 2 introduced the registerContentHandler() and registerProtocolHandler() methods to the navigator object. (These are now formally defined in HTML 5.) These methods allow a website to indicate that it can handle specific types of information. With the rise of online RSS readers and online e-mail applications, this is a way for those applications to be used by default just as desktop applications are used.

The registerContentHandler() method accepts three arguments: the MIME type to handle, the URL of the page that can handle that MIME type, and the name of the application. For instance, to register a site as a handler of RSS feeds, you can use the following code:

navigator.registerContentHandler("application/rss+xml",
"http://www.somereader.com?feed=%s", "Some Reader");

The first argument is the MIME type for RSS feeds. The second argument is the URL that should receive the RSS-feed URL. In this second argument, the %s represents the URL of the RSS feed, which the browser inserts automatically. The next time a request is made for an RSS feed, the browser will navigate to the URL specified and the web application can handle the request in the appropriate way.

image

Firefox through version 4 allows only three MIME types to be used in registerContentHandler(): "application/rss+xml", "application/atom+xml", and "application/vnd. Mozilla.maybe.feed". All three do the same thing: register a handler for all RSS and Atom feeds.

A similar call can be made for protocols by using registerProtocolHandler(), which also accepts three arguments: the protocol to handle (i.e., "mailto" or "ftp"), the URL of the page that handles the protocol, and the name of the application. For example, to register a web application as the default mail client, you can use the following:

navigator.registerProtocolHandler("mailto",
"http://www.somemailclient.com?cmd=%s", "Some Mail Client");

In this example, a handler is registered for the mailto protocol, which will now point to a web-based e-mail client. Once again, the second argument is the URL that should handle the request, and %s represents the original request.

image

In Firefox 2, registerProtocolHandler() was implemented but does not work. Firefox 3 fully implemented this method.

The screen Object

The screen object (also a property of window) is one of the few JavaScript objects that have little to no programmatic use; it is used purely as an indication of client capabilities. This object provides information about the client’s display outside the browser window, including information such as pixel width and height. Each browser provides different properties on the screen object. The following table indicates the properties and which browsers support them.

image

This information is often aggregated by site-tracking tools that measure client capabilities, but typically it is not used to affect functionality. This information is sometimes used to resize the browser to take up the available space in the screen as follows:

window.resizeTo(screen.availWidth, screen.availHeight);

As noted previously, many browsers turn off the capability to resize the browser window, so this code may not work in all circumstances.

Mobile devices behave a little differently with respect to screen dimensions. A device running iOS will always return dimensions as if the device is being held in portrait mode (1024 × 768). Android devices, on the other hand, properly adjust the values of screen.width and screen.height.

The history Object

The history object represents the user’s navigation history since the given window was first used. Because history is a property of window, each browser window, tab, and frame has its own history object relating specifically to that window object. For security reasons, it’s not possible to determine the URLs that the user has visited. It is possible, however, to navigate backwards and forwards through the list of places the user has been without knowing the exact URL.

The go() method navigates through the user’s history in either direction, backward or forward. This method accepts a single argument, which is an integer representing the number of pages to go backward or forward. A negative number moves backward in history (similar to clicking the browser’s Back button), and a positive number moves forward (similar to clicking the browser’s Forward button). Here’s an example:

//go back one page
history.go(-1);
//go forward one page
history.go(1);
//go forward two pages
history.go(2);

The go() method argument can also be a string, in which case the browser navigates to the first location in history that contains the given string. The closest location may be either backward or forward. If there’s no entry in history matching the string, then the method does nothing, as in this example:

//go to nearest wrox.com page
history.go("wrox.com");
//go to nearest nczonline.net page
history.go("nczonline.net");

Two shortcut methods, back() and forward(), may be used in place of go(). As you might expect, these mimic the browser Back and Forward buttons as follows:

//go back one page
history.back();
//go forward one page
history.forward();

The history object also has a property, length, which indicates how many items are in the history stack. This property reflects all items in the history stack, both those going backward and those going forward. For the first page loaded into a window, tab, or frame, history.length is equal to 0. By testing for this value as shown here, it’s possible to determine if the user’s start point was your page:

if (history.length == 0){
//this is the first page in the user's window
}

Though not used very often, the history object typically is used to create custom Back and Forward buttons and to determine if the page is the first in the user’s history. HTML5 further augments the history object. See Chapter 16 for more information.

image

Entries are made in the history stack whenever the page’s URL changes. For Internet Explorer 8+, Opera, Firefox, Safari 3+, and Chrome, this includes changes to the URL hash (thus, setting location.hash causes a new entry to be inserted into the history stack for these browsers).

Summary

The Browser Object Model (BOM) is based on the window object, which represents the browser window and the viewable page area. The window object doubles as the ECMAScript Global object, so all global variables and functions become properties on it, and all native constructors and functions exist on it initially. This chapter discussed the following elements of the BOM:

  • When frames are used, each frame has its own window object and its own copies of all native constructors and functions. Each frame is stored in the frames collection, indexed both by position and by name.
  • To reference other frames, including parent frames, there are several window pointers.
  • The top object always points to the outermost frame, which represents the entire browser window.
  • The parent object represents the containing frame, and self points back to window.
  • The location object allows programmatic access to the browser’s navigation system. By setting properties, it’s possible to change the browser’s URL piece by piece or altogether.
  • The replace() method allows for navigating to a new URL and replacing the currently displayed page in the browser’s history.
  • The navigator object provides information about the browser. The type of information provided depends largely on the browser being used, though some common properties, such as userAgent, are available in all browsers.

Two other objects available in the BOM perform very limited functions. The screen object provides information about the client display. This information is typically used in metrics gathering for web sites. The history object offers a limited peek into the browser’s history stack, allowing developers to determine how many sites are in the history stack and giving them the ability to go back or forward to any page in the history.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.97.75