The Browser Object Model groups browser-provided objects such as
window,location,history,navigator, andscreenthat page scripts use beyond direct document manipulation.
What Is the Browser Object Model?
“BOM” is mainly a teaching category. The relevant interfaces are standardized across HTML, DOM, CSSOM View, and other Web API specifications. Avoid claiming that the BOM is a separate modern specification or that every browser API sits below window in a formal tree.
A useful interview mental model separates three sources of functionality:
-
ECMAScript defines the JavaScript language. Variables, functions, arrays, modules, and promises belong here. A question about promise aggregation, for example, is primarily a language question covered in a
Promise.allinterview guide. -
The DOM represents a document as objects that JavaScript can inspect and change. Elements, text nodes, attributes, and document events belong here.
document.querySelector()is a DOM operation. -
Browser Web APIs provide capabilities supplied by the host environment.
location,history,navigator, timers, storage references, and viewport measurements fall into this group. Interview material often calls a useful subset of these APIs the BOM.
These groups meet in ordinary page code. JavaScript supplies the syntax, document provides the DOM entry point, and browser APIs provide navigation or window behavior. Separating them makes broad JavaScript interview questions easier to classify and explain.
Window, Global Scope, and the DOM Boundary
The Window interface represents a browser window containing a DOM document. Its document property points to that document, so window.document and the global document binding lead to the same DOM entry point in ordinary page scripts. Each browsing context has its own global environment and WindowProxy, whose active Window represents the current document. Navigation can replace that active Window. A tab has a top-level browsing context, and every iframe creates a separate browsing context. The Window reference documents this relationship directly.
Many window properties can be accessed without the window. prefix:
window.location.href;
location.href;
window.setTimeout(runTests, 100);
setTimeout(runTests, 100);
The shorter form works because these names are available through the page's global environment. Keeping window. can still help when an interview answer needs to make the browser dependency obvious.
globalThis is the standard cross-environment way to access the global this value. It corresponds to the page global in a browser, but the name also works in environments that do not provide window. This distinction matters when a utility may run in both a page and a worker.
Global declarations contain a frequent interview trap. At the top level of a classic script, var and function declarations can create properties on the global object. Top-level let and const declarations create global bindings without creating matching global-object properties.
This standalone prediction harness evaluates its source as a global script:
(0, eval)(`
var interviewVar = "var property";
let interviewLet = "let binding";
const interviewConst = "const binding";
function interviewFunction() {}
`);
console.log(globalThis.interviewVar);
console.log("interviewLet" in globalThis);
console.log("interviewConst" in globalThis);
console.log(typeof globalThis.interviewFunction);
delete globalThis.interviewVar;
delete globalThis.interviewFunction;
var property
false
false
function
The result does not mean interviewLet and interviewConst failed to exist inside the evaluated script. They existed as lexical bindings, but they were not properties of globalThis.
This rule needs its classic-script qualification. Module declarations do not become window properties, and top-level this is undefined in a module.
An iframe also has its own window and document. Code inside the frame sees that frame's global environment, viewport, URL, and document rather than automatically using the outer page's values. Access between the frame and its parent then depends on origin and sandbox restrictions.
The Browser Objects You Should Know
An interview answer should connect each object to a task and a limitation. Reciting property names does not show when the API is appropriate.
| Interface or API | What it represents | Realistic use | Interview caveat |
|---|---|---|---|
Location | The current location and its URL components | Read a query string or navigate after sign-in | Navigation can replace the document and interrupt the running exercise |
History | Session history for the current tab or frame | Restore views when Back and Forward are used in an application | Scripts cannot read the URLs of every page in the user's history |
Navigator | Information and capabilities exposed by the browser environment | Check whether a required API exists before enabling a control | Parsing userAgent is not a reliable substitute for feature detection |
Screen | Information about the display associated with the window | Inspect display dimensions for a display-specific workflow | Screen width is not the available page viewport |
Window methods and properties | Operations and measurements associated with the current browsing context | Schedule work, scroll, show a prompt, or measure the viewport | Availability and behavior depend on the runtime and browser policy |
localStorage and sessionStorage | References to Web Storage areas | Save a small preference or temporary exercise state | Stored data is separated by origin |
location exposes the complete URL through href and useful components such as origin, pathname, search, and hash. A practice application might read location.search to select a topic, then use URLSearchParams to parse the query instead of splitting the string manually.
history supports two related jobs. back(), forward(), and go() traverse existing session-history entries. pushState() and replaceState() attach serializable application state to entries.
navigator exposes browser and environment information. Its most useful interview lesson is restraint: check for the capability the code intends to call. Do not choose an implementation by searching for a browser name inside navigator.userAgent.
screen describes the display, not the content area available to the page. screen.width returns the screen width in CSS pixels, but operating-system interface areas and browser chrome can leave the page with much less room.
The window object also exposes several practical groups:
-
setTimeout()schedules a callback once, whilesetInterval()schedules repeated callbacks. A timeout delay is not a promise that the callback runs at that exact instant. -
alert(),confirm(), andprompt()request browser dialogs. A prompt can returnnullwhen it is cancelled, and browsers can limit dialog behavior. -
scrollTo()moves the viewport to document coordinates. -
innerWidthandinnerHeightreport layout viewport dimensions. -
open()asks the browser to open or reuse another browsing context. The request can be blocked.
This timer prediction is runnable in a browser or Node:
console.log("start");
setTimeout(() => {
console.log("timer");
}, 0);
console.log("end");
start
end
timer
A zero delay schedules the callback. It does not pause the current script or place the callback between the two synchronous logs. This distinction often appears beside promise ordering in a JavaScript coding interview.
Navigation and History Without Surprises
The Location and History APIs both affect what appears in the address bar, but they solve different problems. Location operations normally navigate to a document. History state operations let an application represent views within session history.
| Operation | What it does | Does the current entry remain available through Back? | Typical use |
|---|---|---|---|
location.href = url | Loads the supplied URL | Yes | Direct navigation expressed as assignment |
location.assign(url) | Loads the supplied URL | Yes | Explicit document navigation |
location.replace(url) | Replaces the current resource | No | A redirect where returning to the old entry would be wrong |
location.reload() | Reloads the current URL | The operation does not create the same navigation choice as assign() | Refreshing the current document |
history.pushState(state, "", url) | Adds an application history entry | Yes | A new view in a single-page interface |
history.replaceState(state, "", url) | Updates the current entry | No additional entry is added | Correcting or initializing the current view state |
Assigning to window.location is equivalent to assigning to location.href. Both load a document as though location.assign() had been called. location.replace() differs because the replaced page is not preserved as an entry that the Back button can revisit. MDN's Location reference compares these operations.
Run navigation and reload examples only in a disposable preview. They can unload the editor and discard unsaved exercise state.
The traversal methods have straightforward relative meanings. history.back() requests the previous entry. history.forward() requests the next entry. history.go(-1) corresponds to moving back one entry, while history.go(1) requests the next entry.
pushState() adds an entry, and replaceState() updates the active entry. Their state objects must be serializable. A supplied URL must remain within the page's origin.
Neither method fires popstate merely because it was called. Traversal to a different active entry can fire popstate, allowing the page to render the state stored with that entry. The History API guide demonstrates this sequence.
This standalone browser preview creates two view controls and restores their state during history traversal:
const heading = document.createElement("h3");
const output = document.createElement("p");
const allButton = document.createElement("button");
const savedButton = document.createElement("button");
heading.textContent = "Question view";
allButton.type = "button";
allButton.textContent = "Show all questions";
savedButton.type = "button";
savedButton.textContent = "Show saved questions";
document.body.append(heading, allButton, savedButton, output);
function renderView(state) {
output.textContent =
state.view === "saved"
? "Showing saved questions"
: "Showing all questions";
}
function selectView(view) {
const state = { view };
history.pushState(state, "", `?view=${encodeURIComponent(view)}`);
renderView(state);
}
history.replaceState({ view: "all" }, "", location.href);
renderView(history.state);
allButton.addEventListener("click", () => selectView("all"));
savedButton.addEventListener("click", () => selectView("saved"));
window.addEventListener("popstate", (event) => {
renderView(event.state ?? { view: "all" });
});
The initial replaceState() gives the starting entry restorable state. Each selection then uses pushState() because it represents a new view that Back should revisit.
Viewport, Screen, and Browser Detection
Four width properties answer four different questions:
| Property | Measurement |
|---|---|
screen.width | Width of the screen in CSS pixels |
window.outerWidth | Width of the outside of the browser window, including browser chrome |
window.innerWidth | Width of the layout viewport, including a vertical scrollbar when present |
document.documentElement.clientWidth | In standards mode, width of the document viewport excluding the vertical scrollbar |
There is no useful fixed output for these properties. The values change with the display, browser window, scrollbar, zoom behavior, iframe context, and device.
For JavaScript layout calculations, innerWidth usually answers a viewport question more directly than screen.width. In standards mode, document.documentElement.clientWidth provides the viewport width without the vertical scrollbar; in quirks mode, use document.body.clientWidth for that measurement. Choose between these properties based on whether the scrollbar should count. The viewport documentation explains the measurement boundaries.
Responsive presentation normally depends on available viewport space rather than the total display width. A browser can occupy only part of a large screen. An iframe can have a narrow viewport even when its containing tab is wide.
Feature detection asks whether the required capability exists:
const canWriteToClipboard =
typeof navigator !== "undefined" &&
typeof navigator.clipboard?.writeText === "function";
if (canWriteToClipboard) {
// Enable the copy control.
} else {
// Keep a manual selection fallback available.
}
The weaker approach branches on a browser name found in navigator.userAgent. That string can contain overlapping or misleading identifiers, so the inferred browser name does not reliably answer whether one API is usable. MDN recommends testing the required feature.
A successful presence check also does not promise that an operation will succeed. Permissions, policy, context, or user choice can still prevent it. Feature detection decides whether attempting the capability makes sense. Error handling covers the attempt itself.
Security and Runtime Constraints
Browser APIs operate within browsing contexts and security boundaries. An origin is determined by scheme, host, and port. Changing any one of those components can make two documents cross-origin.
The same-origin policy restricts what one window can inspect in another. A script that opens a same-origin page can interact with it more broadly. If the opened page is cross-origin, access to its document and many window properties is restricted. Holding a window reference does not remove that boundary.
Browser storage follows origin boundaries as well. JavaScript from one origin cannot read or write another origin's Web Storage area. An iframe uses the storage associated with the iframe's origin, subject to browser policy and sandbox restrictions. MDN documents origin separation for browser storage.
Popup behavior adds another runtime constraint. window.open() can return null when the browser blocks the request. Popup policies generally expect the call to happen directly in response to user input, so moving the call into a delayed callback can change whether it is allowed.
This standalone browser example creates an accessible control and handles the nullable result:
const openButton = document.createElement("button");
const popupStatus = document.createElement("p");
openButton.type = "button";
openButton.textContent = "Open the Job Board practice question";
popupStatus.setAttribute("role", "status");
document.body.append(openButton, popupStatus);
openButton.addEventListener("click", () => {
const popup = window.open(
"/questions/job-board/",
"practiceQuestion"
);
popupStatus.textContent =
popup === null
? "The browser blocked the practice window."
: "The practice window opened.";
});
The call remains inside the click handler, and the code checks the return value before attempting to use it. The window.open() documentation describes blocking and cross-origin restrictions.
An iframe introduces another context boundary. Its window, document, location, history, and viewport measurements belong to the frame. Parent access depends on origin and iframe restrictions.
Code can also run where window or document does not exist. Server rendering, workers, and some test environments require browser-dependent code to be guarded or postponed:
function getViewportWidth() {
if (
typeof window === "undefined" ||
typeof document === "undefined"
) {
return null;
}
if (document.compatMode === "CSS1Compat") {
return document.documentElement.clientWidth;
}
return document.body ? document.body.clientWidth : null;
}
Returning null states that no page viewport is available in the current runtime. A caller must handle that result instead of treating it as a width.
Browser Object Model Interview Exercises
Use these drills to practise prediction, implementation, and explanation. A library of worked frontend interview problems can supply longer follow-up exercises, while UIReady Premium Lifetime access is useful when repeated browser-editor practice and full test cases are needed.
Predict global-object properties
Run this as a classic script, not as a module. Predict the three booleans before executing it.
<script>
var topic = "bom";
let mode = "practice";
function beginRound() {}
console.log("topic" in window);
console.log("mode" in window);
console.log("beginRound" in window);
</script>
Stop and write down the output before opening the answer.
Expected output and reasoning
true
false
true
The top-level var declaration and function declaration create global-object properties in a classic script. The let declaration creates a global lexical binding without a matching window property.
Predict URL parsing
Predict all four lines before running this standalone snippet.
const url = new URL(
"https://practice.example/interviews/bom?round=2&mode=live#results"
);
console.log(url.origin);
console.log(url.pathname);
console.log(url.searchParams.get("round"));
console.log(url.hash);
Stop and write down the output before opening the answer.
Expected output and reasoning
https://practice.example
/interviews/bom
2
#results
The origin, path, query parameters, and fragment are separate URL components. location exposes corresponding information for the current page.
Predict timer ordering
Predict the output before running this standalone snippet.
console.log("first");
setTimeout(() => {
console.log("third");
}, 0);
console.log("second");
Stop and write down the output before opening the answer.
Expected output and reasoning
first
second
third
setTimeout() schedules its callback. The current synchronous script finishes before the scheduled callback runs.
Choose the correct navigation operation
A sign-in callback should send the user to a dashboard without leaving the callback page as a useful Back destination.
Expected answer: use location.replace(). Assignment to location.href or calling assign() preserves the previous entry for Back navigation.
Explain the history event
A handler calls history.pushState({ view: "saved" }, "", "?view=saved"), but its popstate listener does not run immediately.
Expected answer: pushState() does not fire popstate. Traversal to another active entry can fire it.
Implement feature detection and safe popup handling
Complete the two TODOs in this starter. The copy control must remain hidden unless navigator.clipboard.writeText exists. The popup control must call window.open() inside its click handler and report a blocked null result in the status element.
<button id="copy" hidden>Copy URL</button>
<button id="popup">Open practice window</button>
<p id="status" aria-live="polite"></p>
<script>
const copyButton = document.querySelector("#copy");
const popupButton = document.querySelector("#popup");
const status = document.querySelector("#status");
// TODO: reveal and wire up copyButton only when its method exists.
// TODO: open the popup from this click handler and report null.
popupButton.addEventListener("click", () => {});
</script>
Before opening the solution, verify that the copy button stays hidden when the clipboard method is unavailable and that a blocked popup produces a visible message.
Reference solution
const canCopy =
typeof navigator.clipboard?.writeText === "function";
if (canCopy) {
copyButton.hidden = false;
copyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(location.href);
status.textContent = "URL copied.";
} catch {
status.textContent = "The URL could not be copied.";
}
});
}
popupButton.addEventListener("click", () => {
const popup = window.open("/practice/", "practice");
status.textContent = popup
? "Practice window opened."
: "The popup was blocked.";
});
Capability checks replace browser-name guesses, while the popup code still handles runtime policy after the feature exists.