JavaScriptIntermediate

JavaScript BOM: window, location, history, navigator

Learn the Browser Object Model: use window, location, history, navigator and screen to read URLs, go back, detect offline status and measure screens.

All JavaScript lessons

What you will learn

In the DOM lessons you worked with the page: headings, buttons, lists. But a browser is more than a page. It also has an address bar, a back button, a window that can be resized, information about the device and about the internet connection.

JavaScript can talk to all of these through the Browser Object Model, or BOM. In this lesson you will meet its five most important objects: window, location, history, navigator and screen. You will read query strings, redirect users, build a back button, detect when the internet goes away, and find out how big the screen is.

DOM vs BOM

These two names are easy to mix up.

DOM BOM
Stands for Document Object Model Browser Object Model
Represents The page (HTML elements) The browser (window, address bar, history, device)
Starting object document window
Example document.getElementById("title") window.innerWidth, location.href
Standard Official standard Mostly de-facto standard (all browsers agree)

The BOM has no single official specification, but every browser supports the parts in this lesson in the same way.

window                      <- the browser tab (global object)
├── document                <- the page (the DOM)
├── location                <- the URL in the address bar
├── history                 <- the pages visited in this tab
├── navigator               <- information about the browser and device
├── screen                  <- information about the monitor
└── setTimeout, alert, localStorage, console ...

How to try the examples

Some examples read the current address of the page. The MyCodeNest editor runs your code in a sandboxed preview, so values like location.href look different there (they show the editor’s own internal address). To see real values, press F12, open the Console tab on any website, and type the code there.

To keep the examples runnable everywhere, many of them use the URL object. It splits an address into the same parts that location has. Anything you learn about new URL(...) works for location too.

Safety note: Code that moves to another page or reloads the page is placed inside button clicks in this lesson. If you put location.reload() at the top level of a script, the page reloads, runs the script again, reloads again, and never stops.

window: the global object

window represents the browser tab. It is the top of everything. All global things you use every day belong to it: document, alert, setTimeout, console, localStorage and more. You can leave out window. for these:

window.console.log("Hello");   // same as console.log("Hello")
window.alert === alert;        // true
window.document === document;  // true

Global variables and window

Variables declared with var at the top level (not inside a function) become properties of window. Variables declared with let and const do not.

var oldStyle = "I am on window";
let modern = "I am not on window";

console.log(window.oldStyle);   // "I am on window"
console.log(window.modern);     // undefined

This is one more reason to prefer let and const, so your variables do not collide with the browser’s own names.

Window size

console.log(window.innerWidth);    // width of the visible page area in pixels
console.log(window.innerHeight);   // height of the visible page area
console.log(window.outerWidth);    // width of the whole browser window (with borders)
console.log(window.devicePixelRatio);   // 1 on normal screens, 2 or more on sharp (retina) screens

React to the window being resized with the resize event:

const sizeEl = document.createElement("p");
document.body.append(sizeEl);

function showSize() {
  sizeEl.textContent = window.innerWidth + " x " + window.innerHeight + " px";
}

showSize();
window.addEventListener("resize", showSize);

Resize the preview and the numbers update. For layout changes, prefer CSS media queries. Use JavaScript only when logic needs the size, such as redrawing a chart.

Scrolling

console.log(window.scrollX);   // how far the page is scrolled sideways
console.log(window.scrollY);   // how far the page is scrolled down

To scroll with code:

window.scrollTo({ top: 0, behavior: "smooth" });     // go to the top
window.scrollBy({ top: 300, behavior: "smooth" });   // go down 300px from here

Real use case: a “back to top” button

Show the button only after the user has scrolled down a bit:

<button id="to-top" hidden>&uarr; Top</button>
#to-top {
  position: fixed;
  right: 16px;
  bottom: 16px;
}
const toTop = document.getElementById("to-top");

window.addEventListener("scroll", function () {
  toTop.hidden = window.scrollY < 300;
});

toTop.addEventListener("click", function () {
  window.scrollTo({ top: 0, behavior: "smooth" });
});

Timers also live on window

setTimeout, setInterval, clearTimeout and clearInterval are all window methods:

const id = setTimeout(function () {
  console.log("Runs after 2 seconds");
}, 2000);

clearTimeout(id);   // changed my mind

Opening and closing windows

const button = document.createElement("button");
button.textContent = "Open docs in a new tab";
document.body.append(button);

button.addEventListener("click", function () {
  window.open("https://developer.mozilla.org", "_blank", "noopener");
});

window.open() must run because of a user click. If it runs by itself, popup blockers stop it. Always add "noopener" for links to other websites, so the new page cannot control your page.

location: the address bar

location holds the current URL and lets you change it. Take this address:

https://mycodenest.in:8080/blogs/javascript-arrays/?page=2&sort=asc#examples

Here is how it is split. The code uses new URL(...), which has exactly the same property names as location:

const url = new URL("https://mycodenest.in:8080/blogs/javascript-arrays/?page=2&sort=asc#examples");

console.log(url.href);       // the full address
console.log(url.protocol);   // "https:"
console.log(url.host);       // "mycodenest.in:8080"
console.log(url.hostname);   // "mycodenest.in"
console.log(url.port);       // "8080"
console.log(url.origin);     // "https://mycodenest.in:8080"
console.log(url.pathname);   // "/blogs/javascript-arrays/"
console.log(url.search);     // "?page=2&sort=asc"
console.log(url.hash);       // "#examples"
Property Meaning Example value
href The complete URL the whole address
protocol How to connect "https:"
hostname The website’s name "mycodenest.in"
port Port number (empty if default) "8080"
origin Protocol + host + port "https://mycodenest.in:8080"
pathname The page’s path "/blogs/javascript-arrays/"
search The query string, with ? "?page=2&sort=asc"
hash The fragment, with # "#examples"

On a real page you write location.pathname, location.search, and so on.

Real use case: read query parameters

Query strings carry small bits of information, like which page of results to show. The URLSearchParams object makes them easy to read. Do not split strings by hand.

const url = new URL("https://mycodenest.in/search?q=javascript&page=2&tag=dom&tag=events");
const params = url.searchParams;

console.log(params.get("q"));        // "javascript"
console.log(params.get("page"));     // "2"  (always a string)
console.log(params.get("missing"));  // null
console.log(params.has("page"));     // true
console.log(params.getAll("tag"));   // ["dom", "events"]

for (const [key, value] of params) {
  console.log(key, "=", value);
}

On a live page, the same thing is:

const params = new URLSearchParams(location.search);
const page = Number(params.get("page")) || 1;   // default to page 1

Build and change query strings

const url = new URL("https://mycodenest.in/search");

url.searchParams.set("q", "hello world");
url.searchParams.append("tag", "dom");
url.searchParams.append("tag", "events");

console.log(url.href);
// https://mycodenest.in/search?q=hello+world&tag=dom&tag=events

Notice how the space became +. The object handles encoding (special characters) for you. This is much safer than gluing strings together with +.

Moving to another page

These actions leave the current page, so keep them inside functions that run on purpose:

const goBtn = document.createElement("button");
goBtn.textContent = "Go to the blog";
document.body.append(goBtn);

goBtn.addEventListener("click", function () {
  location.href = "/blogs/";          // go there (Back button returns here)
  // location.assign("/blogs/");      // same as the line above
  // location.replace("/login/");     // go there, but REPLACE this page in history
  // location.reload();               // reload the current page
});
Way Adds to history? Use for
location.href = url or location.assign(url) Yes Normal navigation
location.replace(url) No Redirects, e.g. after login, so Back does not return to the login page
location.reload() No Refresh the page

Changing only the hash

Changing location.hash scrolls to an element with that id without reloading the page:

location.hash = "examples";   // jumps to <... id="examples">

Real use case: redirect after login

function onLoginSuccess() {
  // replace: the login page should not be in the Back button history
  location.replace("/dashboard/");
}

history: the browser’s page list

history knows which pages this tab has visited. For privacy, JavaScript cannot read the actual addresses. It can only move around and add entries.

console.log(history.length);   // how many entries are in this tab's history
const backBtn = document.createElement("button");
backBtn.textContent = "Go back";
document.body.append(backBtn);

backBtn.addEventListener("click", function () {
  history.back();        // same as the browser's Back button
  // history.forward();  // same as Forward
  // history.go(-2);     // go back two pages
  // history.go(1);      // go forward one page
  // history.go(0);      // reload
});

If there is no page to go back to, history.back() does nothing. So check history.length > 1 before showing a Back button.

pushState and replaceState: change the URL without reloading

This is how modern single-page apps (React, Vue, and many more) change the address bar while staying on one page.

history.pushState({ page: 2 }, "", "?page=2");

The three arguments:

  1. state: any small object you want to remember for this entry.
  2. title: ignored by browsers, always pass "".
  3. url: the new address (must be on the same website).

pushState adds a history entry. replaceState has the same arguments but replaces the current one.

history.replaceState({ page: 1 }, "", "?page=1");

When the user presses Back or Forward, the browser does not reload. It fires a popstate event, and you update the page yourself:

window.addEventListener("popstate", function (event) {
  console.log("User navigated. State:", event.state);
});

Real use case: tabs that remember their state

Update the address when a tab is chosen, so that the user can copy the link, refresh, or press Back:

function showTab(name) {
  console.log("Showing tab:", name);
  // ...show/hide the correct panels here
}

function selectTab(name) {
  showTab(name);
  history.pushState({ tab: name }, "", "?tab=" + name);
}

window.addEventListener("popstate", function (event) {
  const tab = event.state ? event.state.tab : "overview";
  showTab(tab);
});

Note: In sandboxed previews, pushState can throw a SecurityError. Wrap it in try...catch there. On a normal website it works fine.

navigator describes the browser, the user’s settings and what the device can do.

console.log(navigator.language);     // "en-US", "hi-IN" ...
console.log(navigator.languages);    // ["en-US", "en"]
console.log(navigator.onLine);       // true or false
console.log(navigator.cookieEnabled);   // true or false
console.log(navigator.hardwareConcurrency);   // number of CPU cores

Real use case: online and offline banner

navigator.onLine tells you whether the browser thinks it has a network. The online and offline events tell you when that changes:

<p>Network: <span id="online-status" class="online">Online</span></p>
#online-status.online  { background: #dcfce7; color: #166534; }
#online-status.offline { background: #fee2e2; color: #991b1b; }
const status = document.getElementById("online-status");

function showStatus() {
  status.textContent = navigator.onLine ? "Online" : "Offline";
  status.className = navigator.onLine ? "online" : "offline";
}

showStatus();
window.addEventListener("online", showStatus);
window.addEventListener("offline", showStatus);

Turn off your Wi-Fi to see it change (you can also use the Network tab in DevTools and choose Offline). Note that onLine: true only means “connected to a network”. It does not promise that the internet works.

Real use case: show prices in the user’s language

const price = 1234567.89;
const language = navigator.language;

console.log(new Intl.NumberFormat(language).format(price));
// "1,234,567.89" for en-US,  "12,34,567.89" for en-IN

Real use case: copy to clipboard

const copyBtn = document.createElement("button");
copyBtn.textContent = "Copy page address";
document.body.append(copyBtn);

copyBtn.addEventListener("click", async function () {
  try {
    await navigator.clipboard.writeText(window.location.href);
    console.log("Copied!");
  } catch (error) {
    console.log("Copy is not allowed here:", error.name);
  }
});

The clipboard needs a secure page (HTTPS) and usually a user click. That is why the code lives inside a click handler and uses try...catch. The async and await words come from the asynchronous JavaScript lessons.

Other useful navigator features

Feature What it does Note
navigator.clipboard.writeText() Copy text Needs HTTPS and a click
navigator.share() Open the phone’s share sheet Mostly mobile
navigator.geolocation.getCurrentPosition() Get the user’s location Browser asks permission first
navigator.mediaDevices.getUserMedia() Use the camera or microphone Permission needed
navigator.userAgent A text describing the browser See the warning below

Check for a feature, do not guess the browser

You will see old code that reads navigator.userAgent to detect Chrome or Safari. Do not do this. The text is long, messy and often wrong. Instead, check whether the feature exists:

if ("share" in navigator) {
  console.log("This device can open the share sheet");
} else {
  console.log("Show copy-link buttons instead");
}

if ("geolocation" in navigator) {
  console.log("Location is supported");
}

This is called feature detection, and it keeps working as browsers change.

screen: the monitor

screen describes the user’s display, not the browser window.

console.log(screen.width);         // total screen width in pixels
console.log(screen.height);        // total screen height
console.log(screen.availWidth);    // width minus things like a side taskbar
console.log(screen.availHeight);   // height minus things like the taskbar
console.log(screen.colorDepth);    // usually 24
console.log(screen.orientation.type);   // "landscape-primary" or "portrait-primary"

Screen, window or page: which size?

Beginners often grab the wrong one. Here is the difference:

What you want Use
The size of the user’s monitor screen.width / screen.height
The size of the visible page area (what your layout fits into) window.innerWidth / window.innerHeight
The width of the page without the scrollbar document.documentElement.clientWidth
The size of the whole browser window (with borders and toolbars) window.outerWidth / window.outerHeight

For responsive design you almost always want window.innerWidth, not screen.width. A user on a big monitor may have a small window.

Real use case: phone-sized window check

function isSmallScreen() {
  return window.innerWidth < 768;
}

console.log(isSmallScreen() ? "Mobile layout" : "Desktop layout");

A better tool for this is window.matchMedia, which uses the same rules as CSS media queries and can notify you when they change:

const query = window.matchMedia("(max-width: 768px)");

console.log(query.matches);   // true on small windows

query.addEventListener("change", function (event) {
  console.log(event.matches ? "Switched to mobile" : "Switched to desktop");
});

You can even ask for the user’s colour preference: window.matchMedia("(prefers-color-scheme: dark)").matches.

Other BOM objects you will meet

The BOM has more members. They are used in later lessons, so here is only a quick map:

Object What it is for
localStorage and sessionStorage Save small pieces of data in the browser
document.cookie Read and write cookies
fetch() Request data from a server
performance Measure how fast things are
window.print() Open the print dialog

Putting it all together: a small “page info” panel

This builds a developer-style information box using several BOM objects at once. It works anywhere:

<pre id="info"></pre>
const url = new URL("https://mycodenest.in/blogs/javascript-arrays/?page=2&sort=asc#examples");

document.getElementById("info").textContent =
  "Site: " + url.hostname + "\n" +
  "Path: " + url.pathname + "\n" +
  "Page param: " + url.searchParams.get("page") + "\n" +
  "Language: " + navigator.language + "\n" +
  "Online: " + navigator.onLine + "\n" +
  "Window: " + window.innerWidth + " x " + window.innerHeight + "\n" +
  "Screen: " + screen.width + " x " + screen.height;

Common mistakes

  • Confusing window and document. window is the browser tab. document is the page inside it. window.document is the same as document.
  • Putting location.reload() or location.href = ... at the top of a script. The page reloads or leaves immediately, again and again. Use them inside a function or event handler.
  • Using location.href for a redirect after login. The user can press Back and land on the login page again. Use location.replace().
  • Parsing query strings by hand. Use URLSearchParams. It handles +, %20, repeated keys and special characters.
  • Forgetting that params.get() returns a string or null. Convert with Number() and give a default: Number(params.get("page")) || 1.
  • Reading navigator.userAgent to detect features. Use feature detection like "share" in navigator.
  • Trusting navigator.onLine too much. true only means a network is connected, not that the server answers. Handle failed requests too.
  • Using screen.width for responsive design. Use window.innerWidth or matchMedia. The screen size is not the window size.
  • Calling window.open() without a click. Popup blockers stop it.
  • Opening external links without "noopener". The new page could access your window.
  • Passing a different-site URL to pushState. It only works for the same origin.
  • Expecting the clipboard or location APIs to work without permission or HTTPS. Always use try...catch and handle a “no”.
  • Reading values that differ in the editor preview. In the sandboxed preview location shows an internal address. Test with real pages in DevTools.

Practice

  1. Click Practice in Editor. Resize the preview and watch the window size text update.
  2. Print window.innerWidth, window.outerWidth and screen.width. Why are they different?
  3. Use new URL("https://mycodenest.in/shop?category=books&sort=price&page=3") and print the category, sort and page values.
  4. Build a URL for https://mycodenest.in/search that has q=hello world and lang=en using searchParams. Print the final href.
  5. Write a function getPage(urlString) that returns the page query value as a number, and 1 if it is missing or invalid.
  6. Add a Back button that is hidden when history.length is 1.
  7. Toggle your Wi-Fi (or use DevTools Network → Offline). Check that the status badge changes.
  8. Add a scroll listener that shows a “Back to top” button only after the user scrolls down 200px.
  9. Use matchMedia("(prefers-color-scheme: dark)") to print whether the user prefers dark mode.
  10. Write code that prints "Share" if navigator.share exists and "Copy link" if it does not.
  11. Challenge: build tabs (Overview, Lessons, Reviews) that update ?tab= in the address bar with pushState, and that restore the right tab when Back and Forward are pressed. Test it on a real page, not in the preview.
  12. Challenge: build a “Copy link” button that uses navigator.share() when available and navigator.clipboard.writeText() as a fallback, and shows "Copied!" for two seconds.

Recap

  • The BOM lets JavaScript talk to the browser itself. The DOM is only about the page. window is the top of the BOM.
  • window is the global object. It gives you innerWidth, innerHeight, scrollY, scrollTo(), open(), timers and more. var globals become window properties, let and const do not.
  • location is the current URL. Use pathname, search, hash and others to read it. Use URLSearchParams for query strings. href and assign add a history entry. replace does not. reload refreshes.
  • history moves with back(), forward() and go(). pushState and replaceState change the address without reloading, and popstate fires on Back and Forward.
  • navigator describes the browser and device: language, onLine, clipboard, share, geolocation. Use feature detection, not userAgent.
  • screen describes the monitor. For layout, use window.innerWidth or matchMedia, not screen.width.
  • Keep page-changing code inside event handlers, and wrap permission-based features in try...catch.
  • Next you will use these ideas with real form controls: reading values, checking input and showing helpful error messages.