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.
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>↑ 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:
- state: any small object you want to remember for this entry.
- title: ignored by browsers, always pass
"". - 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,
pushStatecan throw aSecurityError. Wrap it intry...catchthere. On a normal website it works fine.
navigator: the browser and the device
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
windowanddocument.windowis the browser tab.documentis the page inside it.window.documentis the same asdocument. - Putting
location.reload()orlocation.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.hreffor a redirect after login. The user can press Back and land on the login page again. Uselocation.replace(). - Parsing query strings by hand. Use
URLSearchParams. It handles+,%20, repeated keys and special characters. - Forgetting that
params.get()returns a string ornull. Convert withNumber()and give a default:Number(params.get("page")) || 1. - Reading
navigator.userAgentto detect features. Use feature detection like"share" in navigator. - Trusting
navigator.onLinetoo much.trueonly means a network is connected, not that the server answers. Handle failed requests too. - Using
screen.widthfor responsive design. Usewindow.innerWidthormatchMedia. 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...catchand handle a “no”. - Reading values that differ in the editor preview. In the sandboxed preview
locationshows an internal address. Test with real pages in DevTools.
Practice
- Click Practice in Editor. Resize the preview and watch the window size text update.
- Print
window.innerWidth,window.outerWidthandscreen.width. Why are they different? - Use
new URL("https://mycodenest.in/shop?category=books&sort=price&page=3")and print thecategory,sortandpagevalues. - Build a URL for
https://mycodenest.in/searchthat hasq=hello worldandlang=enusingsearchParams. Print the finalhref. - Write a function
getPage(urlString)that returns thepagequery value as a number, and1if it is missing or invalid. - Add a Back button that is hidden when
history.lengthis1. - Toggle your Wi-Fi (or use DevTools Network → Offline). Check that the status badge changes.
- Add a
scrolllistener that shows a “Back to top” button only after the user scrolls down 200px. - Use
matchMedia("(prefers-color-scheme: dark)")to print whether the user prefers dark mode. - Write code that prints
"Share"ifnavigator.shareexists and"Copy link"if it does not. - Challenge: build tabs (
Overview,Lessons,Reviews) that update?tab=in the address bar withpushState, and that restore the right tab when Back and Forward are pressed. Test it on a real page, not in the preview. - Challenge: build a “Copy link” button that uses
navigator.share()when available andnavigator.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.
windowis the top of the BOM. windowis the global object. It gives youinnerWidth,innerHeight,scrollY,scrollTo(),open(), timers and more.varglobals become window properties,letandconstdo not.locationis the current URL. Usepathname,search,hashand others to read it. UseURLSearchParamsfor query strings.hrefandassignadd a history entry.replacedoes not.reloadrefreshes.historymoves withback(),forward()andgo().pushStateandreplaceStatechange the address without reloading, andpopstatefires on Back and Forward.navigatordescribes the browser and device:language,onLine,clipboard,share,geolocation. Use feature detection, notuserAgent.screendescribes the monitor. For layout, usewindow.innerWidthormatchMedia, notscreen.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.