30% offEnding soon

Browser Object Model: A Frontend Interview Guide

19 min read

The Browser Object Model groups browser-provided objects such as window, location, history, navigator, and screen that 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:

  1. 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.all interview guide.

  2. 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.

  3. 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.

Where top-level declarations liveClassic script globalenvironmentwindowpropertiesvarfunctionlexicalbindingsletconstModulescopedeclarationsstay outsidewindowtop-level thisis undefined
Global declarations land in different places depending on script type.

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.

Each context owns its page worldTop-level tabwindowdocumentiframe: separate contextframewindowframedocument
A tab and its iframe contain separate window–document pairs.

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 APIWhat it representsRealistic useInterview caveat
LocationThe current location and its URL componentsRead a query string or navigate after sign-inNavigation can replace the document and interrupt the running exercise
HistorySession history for the current tab or frameRestore views when Back and Forward are used in an applicationScripts cannot read the URLs of every page in the user's history
NavigatorInformation and capabilities exposed by the browser environmentCheck whether a required API exists before enabling a controlParsing userAgent is not a reliable substitute for feature detection
ScreenInformation about the display associated with the windowInspect display dimensions for a display-specific workflowScreen width is not the available page viewport
Window methods and propertiesOperations and measurements associated with the current browsing contextSchedule work, scroll, show a prompt, or measure the viewportAvailability and behavior depend on the runtime and browser policy
localStorage and sessionStorageReferences to Web Storage areasSave a small preference or temporary exercise stateStored 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, while setInterval() schedules repeated callbacks. A timeout delay is not a promise that the callback runs at that exact instant.

  • alert(), confirm(), and prompt() request browser dialogs. A prompt can return null when it is cancelled, and browsers can limit dialog behavior.

  • scrollTo() moves the viewport to document coordinates.

  • innerWidth and innerHeight report 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.

Zero delay is still a waitCall stacklog “start”log “end”Task queuetimer callbackready, waitingcallback enters only after thestack empties
A zero-delay timer waits for the current stack to finish.

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.

OperationWhat it doesDoes the current entry remain available through Back?Typical use
location.href = urlLoads the supplied URLYesDirect navigation expressed as assignment
location.assign(url)Loads the supplied URLYesExplicit document navigation
location.replace(url)Replaces the current resourceNoA redirect where returning to the old entry would be wrong
location.reload()Reloads the current URLThe operation does not create the same navigation choice as assign()Refreshing the current document
history.pushState(state, "", url)Adds an application history entryYesA new view in a single-page interface
history.replaceState(state, "", url)Updates the current entryNo additional entry is addedCorrecting 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.

How the history stack changesstarting entry —activereplaceStatesame entry — activenow stores view: allpushStateview: allview: saved —activeBackview: all — activeagainsaved remainsahead
Replace mutates a history entry; push adds one; Back changes the active entry.

Viewport, Screen, and Browser Detection

Four width properties answer four different questions:

Which boundary does “width” mean?screen.widthouterWidth: browser frameinnerWidth: layout viewportdocumentElement.clientWidthviewport without scrollbarscrollbar counts in innerWidth
The four widths stop at different nested boundaries.
PropertyMeasurement
screen.widthWidth of the screen in CSS pixels
window.outerWidthWidth of the outside of the browser window, including browser chrome
window.innerWidthWidth of the layout viewport, including a vertical scrollbar when present
document.documentElement.clientWidthIn 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.

Two gates, not one promiseDoes the required methodexist?NoYesKeep fallbackdo not call itAttempt callstill not guaranteedAllowedDeniedCapability worksshow successHandlethe error
Feature detection permits an attempt; runtime handling determines the outcome.

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.

Frequently asked questions

What is the Browser Object Model in JavaScript?
The Browser Object Model is a teaching term for browser-provided objects used outside direct document manipulation. Common examples include window, location, history, navigator, and screen.
What is the difference between the DOM and the BOM?
The DOM represents a document and provides APIs for working with its nodes, attributes, and events. The BOM label usually covers browser capabilities such as navigation, session history, viewport measurements, and window management.
Is window the same as globalThis?
In an ordinary browser page, globalThis refers to the page's global object and corresponds to window. globalThis also works in environments without a Window, so it is the safer name when code may run in workers or outside a browser.
Does history.pushState fire popstate?
No. pushState and replaceState change session history without firing popstate themselves. Traversal to another entry, such as pressing Back or calling history.back(), can fire the event.
Should browser detection use navigator.userAgent?
Code should usually test for the capability it needs instead of inferring support from navigator.userAgent. User agent strings can contain overlapping or misleading browser identifiers.