window, location, history, and screen Objects
Introducing the Browser Object Model
- The Browser Object Model (BOM) is an API that provides JavaScript with access to the various properties and methods of the browser window.
- The DOM is at the page level; the BOM is at the browser level.
- The BOM existed first as a de facto standard, since much of it was first introduced in an early version of the Netscape browser and subsequent browsers have supported it. If new browsers dropped support a lot of existing pages would break.
- As the years passed, the Web Hypertext Application Technology Working Group (WHATWG) authored a living standard that included these objects.
- If you check your browser preferences there is invariably a section related to JavaScript that lists BOM properties / methods that you can prevent JavaScript from accessing.
- For example, you could prevent windows from being moved or resized via JavaScript.
The window Object
- While ECMAScript does not specify what the global object (the object that contains all functions and variables in your code) should be, in web browsers this is
windowand represents the browser window. - Every global variable is located directly below that global object.
- A global variable named
testVariableis accessible viawindow.testVariableor simply viatestVariable, because the JavaScript interpreter will move up the object tree until it reaches the very top (which iswindow). - If it moves all the way up to
windowand cannot find a match then an error is thrown. - Most of the time
windowdoes not need to be listed, because the object lookup occurs.- This is a tradeoff between streamlining code (omitting
windowreduces code) and performance (moving up the object tree takes time). - Although rare, it is possible that omitting
windowfor methods will lead to code behaving in unanticipated ways. Those are cases where an object you encounter along the way towindowhas a method with the same name. Typically this is thedocumentobject. - One other consideration is code portability - if your JavaScript is going to run in an environment with a different global object, then hard-coding
windowwill very likely break your code.
- This is a tradeoff between streamlining code (omitting
selfis synonymous withwindow- In frames-based layouts
windowis also synonymous withtopandparent. Let's hope you never have to work with frames. - Every browser window, frame, and iframe has its own separate
windowobject.
Object Hierarchy
The object hierarchy for the Browser Object Model is:
- window
- document
- history
- location
- navigator
- screen
window Object Properties and Methods
| Method / Property | Description | Code Example |
|---|---|---|
| alert() |
Displays a dialog box with the indicated message (a string object).
Used for situations such as warnings to the user; also used for troubleshooting coding errors/issues. JavaScript processing (except for web workers) is halted until this is acknowledged. |
alert('your message'); |
| close() |
Closes the current window; users might disable this capability in their browser preferences.
JavaScript-opened windows will close automatically or may prompt the user. When this is attempted on windows that users opened they will get a prompt asking if the window can close or the browser could simply ignore the command. Specify as |
window.close(); |
| closed |
Returns false if the window has not been closed. If it has been closed, then it returns true.
|
window.closed |
| confirm() |
Passed a string object containing a message / question for the user that is displayed in a dialog box with 'OK' and 'Cancel' buttons.
As soon as the code is parsed the dialog box appears. If the user confirms (clicks 'OK') then If the user does not confirm (clicks 'Cancel') then JavaScript processing (except for web workers) is halted until this is acknowledged. |
const answer = confirm('Should we continue?');
|
| defaultStatus |
Sets a new default for the status bar text. This default is shown until an anchor is moused over (the status bar then displays the href value of that anchor).
Many browsers allow users to disable this property as it can reduce usability and annoy users. Browsers may also have the status bar display entirely turned off. This property is best avoided. |
window.defaultStatus = 'Welcome to my site!';
|
| status | Alters the text in the status bar temporarily; like defaultStatus this is best avoided. |
window.status = 'Hi'; |
| stop() | Shuts down the window loading process. | window.stop(); |
| innerHeight | This property tracks the vertical space inside the browser window; other ways to measure this exist. | alert(window.innerHeight); |
| innerWidth | This property tracks the horizontal space inside the browser window; other ways to measure this exist. | alert(window.innerWidth); |
| moveBy() |
Requires x and y parameters (number objects) to be passed and moves the browser window horizontally and vertically that number of pixels, based on the current location.
No size units (such as px) are given because this is always in pixels. Many browsers allow users to disable this functionality. |
moveBy(10,10); |
| moveTo() |
Requires x and y parameters (number objects) to be passed and moves the browser window to that location, regardless of the current location.
No size units (such as px) are given because this is always in pixels. Many browsers allow users to disable this functionality. |
moveTo(100,200); |
| name |
The name assigned to the window.
Typically specified as a parameter of |
window.name = 'newWindow'; |
| open() |
Opens a new browser window.
Users might disable this in their browser preferences or a pop-up blocker may block all windows opened via JavaScript. Passed three parameters:
Specify as |
window.open('https://www.google.com', '_blank', 'scrollbars=1,resizable=1,location=1'); |
| opener | This property only exists for newly opened browser windows; it references the window that opened the new window. | See the code examples for communicating across windows. |
| postMessage() | This method allows for messages (text strings) to be passed between windows on different domains. Read more about the postMessage() method. | See the code examples for communicating across windows. |
| prompt() |
Displays a dialog box where the user is able to enter data.
Passed two string objects as parameters:
If you would rather not have any default text, set this second value to an empty string ( If 'Cancel' is clicked the value returned is either JavaScript processing (except web workers) is halted until this is resolved. |
let name = prompt('What is your name?', 'Enter name here'); |
| print() | Opens the browser's print dialog. | print(); |
| resizeBy() |
Requires x and y parameters (number objects) to be passed and resizes the window by that number of pixels, based on the current dimensions of the window.
No size units (such as px) are given because this is always in pixels. Many browsers allow users to disable this functionality. |
resizeBy(5,5); |
| resizeTo() |
Requires x and y parameters (number objects) to be passed and resizes the window to that pixel size, regardless of the current dimensions of the window.
No size units (such as px) are given because this is always in pixels. Many browsers allow users to disable this functionality. |
resizeTo(400,400); |
| scrollBy() |
Requires x and y parameters (number objects) and scrolls the browser window horizontally and vertically by that amount.
No size units (such as px) are given because this is always in pixels. |
scrollBy(0,300); |
| scrollTo() |
Requires x and y parameters (number objects) and scrolls the browser window horizontally and vertically to that location, regardless of the current scroll position.
No size units (such as px) are given because this is always in pixels. |
scrollTo(0,300); |
| setInterval() |
Calls a function / method repeatedly, at the indicated interval.
The first parameter is the function / method name. If you pass a parameter or parameters you must surround the function / method name in quotation marks. The second parameter is a number object for the interval, which is in milliseconds. This is very useful in XHR and Fetch setups to send / receive data at the indicated interval. |
const theTimer = setInterval(aFunctionName, 5000);
or const theTimer = setInterval("aFunctionName('param')", 5000);
|
| clearInterval() |
Used to clear the interval created via setInterval().
The parameter passed is the variable representing the interval. |
clearInterval(theTimer); |
| setTimeout() |
Calls a function / method a single time, when the interval elapses.
The first parameter is the function / method name. If you pass a parameter or parameters you must surround the function / method name in quotation marks. The second parameter is a number object for the timeout, which is in milliseconds. This is very useful for timeouts on screens and redirecting to other screens after a certain period of time. |
const aTimer = setTimeout(aFunctionName, 5000);
or const aTimer = setTimeout("aFunctionName('param')", 5000);
|
| clearTimeout() |
Used to clear the interval created via setTimeout().
The parameter passed is the variable representing the timeout. |
clearTimeout(aTimer); |
| requestAnimationFrame() |
Instructs the browser that an animation will occur.
A single parameter is passed to this method, which is a reference to the callback function that will create the animation (through modifying CSS). That callback function, in turn, receives a single parameter, which is the number of milliseconds reflecting the current time. |
requestAnimationFrame(callback); |
Additional Considerations with Timeouts / Intervals
- JavaScript runs as a single process / thread in the browser.
- That means that the JavaScript interpreter can only do one thing at a time.
- Web Workers are an exception to this, as they run in separate threads that operate in parallel with the main thread where JavaScript is running. We are not exploring Web Workers in this article, so the rest of these considerations deal with the main thread JavaScript.
- Timeouts and intervals are scheduled on a timeline out into the future that reflects, at best, an approximate time when they will occur.
- If something else is occupying the main thread JavaScript interpreter at the moment when a timeout or interval elapses, whatever needs to happen when that timer expires will be delayed until the interpreter can get to it. The function will be called eventually, but it will not be at precisely the moment desired.
- Even under good conditions expect the code to execute 10 - 100 milliseconds after the timer has elapsed.
- Any JavaScript dialog box, such as
alert(),confirm(), orprompt(), will also freeze the timeline until those are addressed by the user, so all timers are frozen until those are handled. - From a performance perspective it is faster to call a function than to pass a string, because the function can be optimized during compilation and the string being passed cannot be optimized.
window Events
| Event | Triggered By |
|---|---|
| afterprint | Fires when the print preview has been closed and/or the document has started printing. |
| beforeprint | Fires when the print preview is about to open and when the document is about to print. |
| beforeunload | Fires when the document is about to be unloaded (so before the unload event fires). Mainly used to trigger a warning about losing unsaved data if the user reloads or tries to navigate away after entering data or editing data. |
| blur | Fires when the window loses focus (when another window gains focus). |
| error | Fires when a JavaScript error occurs (such as a security violation or trying to use a non-existent object) or if an object download fails (such as an image not being found). |
| focus | Fires when the window gains focus. |
| hashchange | Fires when the # sign part of the URL changes. |
| load | Fires after everything else on the page has been loaded (HTML, CSS, images, videos, etc.). |
| resize | Fires when the browser window is resized. Resizing by as little as 1 pixel fires this event. A debounce / throttle function is often used to prevent overloading the interpreter, timeline, and browser. These rely on setTimeout(), discarding the vast majority of calls until the timer has elapsed. |
| scroll | Fires when the browser window is scrolled. Scrolling by as little as 1 pixel fires this event. A debounce / throttle function is often used to prevent overloading the interpreter, timeline, and browser. These rely on setTimeout(), discarding the vast majority of calls until the timer has elapsed. |
| unload | Fires when the document in the browser window changes. This event has been deprecated, so browsers may no longer support it. There are better alternatives (such as beforeunload) that function more reliably across a variety of usage scenarios. |
Attributes for window.open()
- The third parameter passed to
window.open()is a list of the attributes controlling the window's location and appearance, separated by commas. - These can be included in any sequence and not all need to be specified.
- Make sure that there are no spaces between attributes and that only one set of quotation marks (either single or double) encloses all the attributes.
- Keep in mind that it is very annoying if an overly large window opens up and its edges are off the side of the user's monitor or viewport. That is a major blow to the user experience.
- Think carefully before disabling
location,scrollbars, andresizable, again due to usability concerns.
| Attribute | Values | Details |
|---|---|---|
| copyhistory |
|
Transfers the old window's history to the new window (if 1 is the value).
Generally not important, but there are times when it is useful. |
| directories |
|
Controls whether the Favorites bar / Personal bar is visible.
Usually not important. |
| height |
|
Height (in pixels) of the content area, which does not include space needed for toolbars and other chrome. |
| left |
|
The number of pixels the window is from the left edge of the monitor. |
| location |
|
Controls whether the location field is visible.
Some browsers allow users to block JavaScript access to this attribute. |
| menubar |
|
On Windows this can turn on (1) or turn off (0) the menu bar at the top of the window.
Macs generally ignore this attribute and have the menubar visible. |
| resizable |
|
Controls whether the user can resize the window.
Either they can (value of |
| scrollbars |
|
Controls whether there are scrollbars visible. |
| status |
|
Controls whether the status bar at the bottom of the layout is visible.
Some browsers allow users to block JavaScript access to this attribute. |
| toolbar |
|
Controls whether the toolbar at the top of the layout is visible. |
| top |
|
The number of pixels the window is from the top edge of the monitor. |
| width |
|
The width (in pixels) of the content area, not counting scrollbars or anything else in the browser chrome. |
Opening New Windows
While CodePen is the preferred environment for hosting code examples for this website (because it allows for the examples to be edited), working with new windows doesn't work well with their architecture.
That is why the code is shown here and is hosted on this domain instead.
Structure (new_window.html)
<!DOCTYPE html> <html lang="en-us"> <head> <title>Opening New Windows</title> <meta charset="utf-8" /> </head> <body> <ul> <li><a href="https://www.google.com">Google</a></li> <li><a href="https://www.amazon.com" class="nWin">Amazon</a></li> <li><a href="https://www.indeed.com">Indeed</a></li> </ul> <script src="new_window.js"></script> </body> </html>
Behavior (new_window.js)
"use strict";
const openNew = {
theBody : document.getElementsByTagName('body')[0],
// attributes for new windows
params : 'top=5,left=5,width=400,height=400,scrollbars=1,resizable=1,location=1',
init : function() {
this.theBody.addEventListener('click', this.openIt, false);
},
openIt : function(evt) {
// pinpoint the anchor clicked
const linkClicked = openNew.findTarget(evt, 'a', this);
// stop if an anchor was not clicked or the anchor lacks the 'nWin' class
if (!linkClicked || !linkClicked.classList.contains('nWin')) { return; }
// shut down the default behavior of the anchor
// without this the existing window also changes its address
evt.preventDefault();
// open the new window
window.open(linkClicked.href, '_blank', openNew.params);
},
findTarget : function(evt, targetNode, container) {
let currentNode = evt.target;
while (currentNode && currentNode !== container) {
if (currentNode.nodeName.toLowerCase() === targetNode.toLowerCase()) {
return currentNode; }
else { currentNode = currentNode.parentNode; }
}
return false;
}
}
openNew.init();
Communicating Across Windows
Two examples are provided, showing different approaches. The first example uses DOM references for updating one window's content based on information from another window.
The second example uses postMessage() for passing information.
The examples are again hosted on this domain rather than CodePen, as their architecture doesn't work well with cross-window communication.
Example #1: window.opener and DOM Lookups
Structure of Starting Page (cross_window.html)
<!DOCTYPE html> <html lang="en-us"> <head> <title>Communicating Across Windows</title> <meta charset="utf-8" /> <link rel="stylesheet" href="cross_window.css" /> </head> <body> <form method="post" action=""> <label for="language">Select a language:</label> <span id="lang_holder"></span> <input type="button" id="language" value="Choose..." /> </form> <script src="cross_window.js"></script> </body> </html>
Structure of Popup Page (language_list.html)
<!DOCTYPE html> <html lang="en-us"> <head> <title>Language List</title> <meta charset="utf-8" /> <link rel="stylesheet" href="cross_window.css" /> </head> <body> <table id="language_list"> <thead> <tr> <th scope="col">Language</th> <th scope="col">Full Name</th> <th scope="col">Side</th> </tr> </thead> <tbody> <tr> <td scope="row"><a href="#">HTML</a></td> <td>Hypertext Markup Language</td> <td>Client</td> </tr> <tr> <td scope="row"><a href="#">CSS</a></td> <td>Cascading Style Sheets</td> <td>Client</td> </tr> <tr> <td scope="row"><a href="#">JavaScript</a></td> <td>JavaScript</td> <td>Client</td> </tr> <tr> <td scope="row"><a href="#">PHP</a></td> <td>PHP: Hypertext Preprocessor</td> <td>Server</td> </tr> </tbody> </table> <script src="cross_window.js"></script> </body> </html>
Presentation for Both Pages (cross_window.css)
body, input {font: 75% verdana, sans-serif;}
input {font-size: 100%;}
table {border-collapse: collapse;}
td, th {padding: 1em; text-align: left; vertical-align: top; border: 1px solid #000;}
#lang_holder {padding-right: 2px;}
Behavior for Both Pages (cross_window.js)
"use strict";
const crossWindow = {
// depending on which of these element nodes is found
// we know which page we are on
languageButton : document.getElementById('language'),
languageList : document.getElementById('language_list'),
// attributes for new windows
params : 'top=40,left=40,width=500,height=500,scrollbars=1,resizable=1,location=1',
init : function() {
// if we are not on the correct page languageButton is null
if (this.languageButton) {
this.languageButton.addEventListener('click', this.openWin, false);
}
// if we are not on the correct page languageList is null
else if (this.languageList) {
this.languageList.addEventListener('click', this.passValueBack, false);
}
},
openWin : function() {
// open the new window and have a variable referencing it
const newWindow = window.open('language_list.html', '_blank', crossWindow.params);
// give the new window focus, on the off chance something put it
// into the background
newWindow.focus();
},
passValueBack : function(evt) {
const linkClicked = crossWindow.findTarget(evt, 'a', this);
if (!linkClicked) { return; }
// pinpoint the span element node in the other window
const holder = window.opener.document.getElementById('lang_holder');
holder.innerHTML = linkClicked.firstChild.nodeValue;
// shut down the new window
window.close();
// shut down the href of the anchor clicked
evt.preventDefault();
},
findTarget : function(evt, targetNode, container) {
let currentNode = evt.target;
while (currentNode && currentNode !== container) {
if (currentNode.nodeName.toLowerCase() === targetNode.toLowerCase()) {
return currentNode; }
else { currentNode = currentNode.parentNode; }
}
return false;
}
}
crossWindow.init();
See how this first cross window example renders
Example #2: window.opener and postMessage()
The structural markup and the CSS have remained the same (other than altering CSS and JS file names).
Revised JavaScript (post_message.js)
"use strict";
const crossWindow = {
// depending on which of these element nodes is found
// we know which page we are on
languageButton : document.getElementById('language'),
languageList : document.getElementById('language_list'),
languageHolder : document.getElementById('lang_holder'),
// attributes for new windows
params : 'top=40,left=40,width=500,height=500,scrollbars=1,resizable=1,location=1',
init : function() {
// if we are not on the correct page languageButton is null
if (this.languageButton) {
this.languageButton.addEventListener('click', this.openWin, false);
}
// if we are not on the correct page languageList is null
else if (this.languageList) {
this.languageList.addEventListener('click', this.generateMessage, false);
}
// listen for post messages
window.addEventListener("message", this.updateText, false);
},
// the data property of this event contains the text of the message
updateText : function(evt) {
if (crossWindow.languageHolder) {
crossWindow.languageHolder.innerHTML = evt.data;
}
},
openWin : function() {
// open the new window and have a variable referencing it
const newWindow = window.open('language_list.html', '_blank', crossWindow.params);
// give the new window focus, on the off chance something put it
// into the background
newWindow.focus();
},
generateMessage : function(evt) {
const linkClicked = crossWindow.findTarget(evt, 'a', this);
if (!linkClicked) { return; }
const theMessage = linkClicked.firstChild.nodeValue;
// send the message to the opening window
// for security reasons the domain must match what is specified
window.opener.postMessage(theMessage, "https://learningtocode.info");
// shut down the new window
window.close();
// shut down the href of the anchor clicked
evt.preventDefault();
},
findTarget : function(evt, targetNode, container) {
let currentNode = evt.target;
while (currentNode && currentNode !== container) {
if (currentNode.nodeName.toLowerCase() === targetNode.toLowerCase()) {
return currentNode; }
else { currentNode = currentNode.parentNode; }
}
return false;
}
}
crossWindow.init();
location Object Properties and Methods
- This object represents the address currently being accessed.
- You must reference
locationas part of these properties and methods. - Note that some of the properties for this object are read-only.
- A generic representation of the location is:
protocol://host:port/pathname#hash?search - In detailing the properties this URL will be used:
https://learningtocode.info/js/?p=bom-window#evts
| Method / Property | Description | Code Example |
|---|---|---|
| protocol | The communication protocol in use. |
alert(location.protocol);
will display: https:
|
| host | The host and domain name visited. |
alert(location.host);
will display: learningtocode.info
|
| port | The server's communication port used for the protocol (usually port 80 for HTTP). | alert(location.port); |
| pathname | The directory and file path. |
alert(location.pathname);
will display: /js/
|
| hash |
The anchor name in the address (e.g., for within-page sections).
Each time the hash changes a new entry is added in the browser history. There is a hashchange event that fires when the hash (the part after the #) is modified. |
alert(location.hash);
will display: #evts
|
| search |
The question mark and everything following it; used for passing variables and their values via the URL.
There is a URLSearchParams API that allows you to work with the variables passed via |
alert(location.search);
will display: ?p=bom-window
|
| href |
The entire URL; very useful when redirecting to a new page / address.
This is done by assigning a new URL to |
alert(location.href);
will display: https://learningtocode.info/js/?p=bom-window#evts
|
| hostname | The host:port part of the URL. |
alert(location.hostname);
will display: learningtocode.info
|
| reload() |
Forces a reload of the current page.
No parameter is passed. |
location.reload(); |
| replace() |
Loads the specified URL (passed as a string object to this method) and replaces the old page in the browser history.
This makes it impossible to use the browser's Back button to return to the previous page. Before implementing this the key question is whether a user needs to go back. If they do not, this is a good approach. If they do need to go back, this is a major usability issue. |
location.replace('https://www.google.com');
|
history Object Properties and Methods
This object provides very limited access to the browser's history data and some history-related functionality.
| Method / Property | Description | Code Example |
|---|---|---|
| back() |
Moves back one page in the browser history, assuming that a page exists there.
If no page exists, nothing will happen. This is used for 'Back' buttons that are part of the web page (not the one built into the browser). |
history.back(); |
| forward() |
Moves ahead one page in the browser history, assuming that a page exists there.
If no page exists, nothing will happen. |
history.forward(); |
| go() |
Passed a number object representing the number of pages to move forward (positive number) or backward (negative number).
Passing Passing Passing This will not work if there is no page at the indicated position in the history. |
history.go(-2); |
| length |
This property tracks the total number of items in the browser history for that page.
It is not possible to read the values in the history (due to privacy concerns). |
alert(history.length); |
The History API
- There is also a History API that allows you to store a lot of data about a given URL variation in a state object.
- This is used extensively in single-page web applications and the URL variations are generally the hash value changing.
- I have used this in filtered product search results (there were around 10 filters that could be used in varying combinations):
- The hash changed each time a filter was added or removed
- The state (basic info about the results; the product id / name / image path, as well as the state of the filters in play) was saved with that new URL variation.
- As the user hit the browser back button the hash would change and I would load the previous results and restore the filter selections.
- Without that setup the back button would have taken the user directly out of the filtered product results, and delving into a product and then hitting the back button would have lost all the filters.
- This also alleviated the need to hit the database again to determine the matches, because the state objects held the product info.
- If there was no data stored for a given hash, then the results were fetched from the database.
- This setup allowed for a remarkably fast search / filter from a dataset with millions of products.
- Read more about working with the History API.
screen Object Properties
- This object provides information about the user's computer screen. This information is often used by web analytics (it is stored in order to learn more about user settings).
- Unfortunately, this is not very useful unless you are altering the layout based on resolution.
| Property | Description | Code Example |
|---|---|---|
| availHeight | Provides the vertical screen resolution with taskbars and other browser chrome removed. | alert(screen.availHeight); |
| availWidth | Provides the horizontal screen resolution with scrollbars and other browser chrome removed. | alert(screen.availWidth); |
| colorDepth | Provides the number of bits per pixel (e.g., 16-bit, 32-bit), although it only reports back the number (such as 32). |
alert(screen.colorDepth); |
| height | Provides the vertical resolution of the screen | alert(screen.height); |
| width | Provides the horizontal resolution of the screen | alert(screen.width); |