JavaScript in a web page doesn't just talk to the HTML. It also talks to the browser itself: the window and its size, the address bar, the back and forward buttons, the clipboard, the user's language and whether they're online. Together these are sometimes called the Browser Object Model (BOM), and they're what let a page behave like an app.
Many everyday features rely on them. A search page reads ?q=shoes from the URL to know what to search for. A filtered product list updates the URL so the filters can be shared and the back button works. A "Copy link" button writes to the clipboard. Images and animations start only when they scroll into view.
In this lesson you'll explore the window object, read and build URLs with location, URL and URLSearchParams, change the address bar without reloading using the History API, use a few handy navigator features, and watch elements appear on screen efficiently with IntersectionObserver.
The window object
window is the global object in the browser. Global variables and functions, document, console, setTimeout and localStorage all live on it, which is why you can write alert() instead of window.alert().
| Property or method | Gives or does |
|---|---|
innerWidth, innerHeight | The size of the visible page area in pixels |
scrollX, scrollY | How far the page is scrolled |
scrollTo({ top: 0, behavior: "smooth" }) | Scrolls the page |
open(url) | Opens a new tab or window |
devicePixelRatio | Screen density (2 on most phones) |
addEventListener("resize" / "scroll", fn) | Reacts to size and scroll changes |
For reacting to screen size, responsive JavaScript with matchMedia is usually better than reading innerWidth.
location and URLs
For the address https://shop.example.com/search?q=lamp&page=2#reviews:
| Property | Value |
|---|---|
location.href | The whole URL |
location.origin | https://shop.example.com |
location.pathname | /search |
location.search | ?q=lamp&page=2 |
location.hash | #reviews |
const params = new URLSearchParams(location.search);
params.get("q"); // "lamp"
params.set("page", 3);
const url = new URL("/search", location.origin);
url.searchParams.set("q", "desk lamp"); // spaces are encoded for youSetting location.href = "/basket" navigates to a new page; location.reload() reloads.
The History API
history.pushState({ page: 2 }, "", "?page=2"); // new history entry, no reload
history.replaceState(null, "", "?sort=price"); // change the current entry
window.addEventListener("popstate", (e) => render(e.state)); // back/forward pressedpushState changes the address bar and adds a step to the back button without loading a new page. When the user presses back or forward, the popstate event fires and your code updates the page to match. This is how single-page apps and filterable lists keep URLs shareable. history.back() and history.forward() move through history in code.
JavaScript Browser APIs Syntax
Browser APIs
window.innerWidth; window.scrollY;
window.scrollTo({ top: 0, behavior: "smooth" });
const q = new URLSearchParams(location.search).get("q");
history.pushState({}, "", `?q=${encodeURIComponent(q)}`);
addEventListener("popstate", () => render());
await navigator.clipboard.writeText(location.href);
new IntersectionObserver(cb, { threshold: 0.5 }).observe(el);JavaScript Browser APIs Summary
windowis the global object, with size, scroll and timer features.locationdescribes the current URL;URLSearchParamsreads and builds query strings.history.pushStatechanges the URL without reloading; handlepopstatefor back and forward.navigatoroffers clipboard, share, language and online status.IntersectionObserverefficiently detects when elements scroll into view.
JavaScript Browser APIs Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Reading and building URLs
const url = new URL("https://shop.example.com/search?q=desk+lamp&page=2&colour=black#reviews");
console.log("Path:", url.pathname);
console.log("Hash:", url.hash);
const params = url.searchParams;
console.log("Search for:", params.get("q"));
console.log("Page:", Number(params.get("page")));
console.log("Has size?", params.has("size"));
params.set("page", 3);
params.append("colour", "white");
params.delete("q");
console.log("New URL:", url.href);
console.log("All colours:", params.getAll("colour"));
const link = new URL("/products", "https://shop.example.com");
link.searchParams.set("name", "café & bar");
console.log(link.href);Line-by-line explanation
- Line 1
URLbreaks any address into parts, just likelocationdoes for the current page. - Line 7
getdecodes values, so the+becomes a space. - Line 8Query values are strings; convert numbers yourself.
- Lines 11 to 13Change, add and remove parameters.
- Line 18Special characters are encoded automatically.
Filters that update the URL
Click the filters. Each click adds a history entry, and the new URL is logged.
<div id="filters">
<button data-cat="all">All</button>
<button data-cat="books">Books</button>
<button data-cat="games">Games</button>
</div>
<p id="state"></p>const state = document.querySelector("#state");
function render(cat) {
state.textContent = `Showing: ${cat}`;
for (const b of document.querySelectorAll("#filters button")) {
b.style.fontWeight = b.dataset.cat === cat ? "bold" : "";
}
}
document.querySelector("#filters").addEventListener("click", (e) => {
const cat = e.target.dataset.cat;
if (!cat) return;
try {
history.pushState({ cat }, "", `?cat=${cat}`);
} catch {
// some sandboxed previews block URL changes
}
render(cat);
console.log("URL would be ?cat=" + cat);
});
window.addEventListener("popstate", (e) => render(e.state?.cat ?? "all"));
render(new URLSearchParams(location.search).get("cat") ?? "all");Line-by-line explanation
- Lines 3 to 8One function draws the page from a state value.
- Line 14Each filter click adds a history entry with the new URL.
- Line 22Back and forward buttons fire
popstate, and the page redraws from the saved state. - Line 23On load, the page reads its starting state from the URL, so links can be shared.
Reveal on scroll with IntersectionObserver
Scroll inside the result panel.
<div id="scroller" style="height: 220px; overflow-y: auto; font-family: sans-serif">
<p style="height: 260px">Scroll down</p>
<div class="reveal">First card</div>
<div class="reveal">Second card</div>
<div class="reveal">Third card</div>
</div>
<style>
.reveal { margin: 0 0 140px; padding: 24px; background: #dbeafe; border-radius: 10px; opacity: 0; transform: translateY(20px); transition: 0.5s; }
.reveal.visible { opacity: 1; transform: none; }
</style>const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
console.log("Revealed:", entry.target.textContent);
observer.unobserve(entry.target);
}
}
}, { root: document.querySelector("#scroller"), threshold: 0.3 });
document.querySelectorAll(".reveal").forEach((el) => observer.observe(el));Line-by-line explanation
- Line 1The callback receives entries for elements whose visibility changed.
- Lines 3 to 4When a card is at least 30% visible, it fades in.
- Line 6Stop watching it once it's revealed.
- Line 9
rootis the scrolling box; leave it out to watch the whole page. - Line 11Start watching every card.
Common JavaScript Browser APIs Mistakes
Building query strings by hand
location.href = "/search?q=" + query; // "rock & roll" breaks the URLconst url = new URL("/search", location.origin);
url.searchParams.set("q", query);
location.href = url;pushState without handling popstate
history.pushState({}, "", "?page=2"); // back button changes the URL but not the pagehistory.pushState({ page: 2 }, "", "?page=2");
addEventListener("popstate", (e) => showPage(e.state?.page ?? 1));Checking positions on every scroll
window.addEventListener("scroll", () => {
cards.forEach(c => { if (c.getBoundingClientRect().top < innerHeight) c.classList.add("visible"); });
});const io = new IntersectionObserver(entries => entries.forEach(e => e.isIntersecting && e.target.classList.add("visible")));
cards.forEach(c => io.observe(c));JavaScript Browser APIs in Real Projects
Browser APIs in practice
Online shops put filters and sort order in the URL so customers can share a search. Single-page apps like Gmail and YouTube use the History API to change pages without full reloads. Blogs lazy-load images and comments with IntersectionObserver, and news sites use it to count articles as read. "Copy link" and "Share" buttons use the clipboard and Web Share APIs.
JavaScript Browser APIs Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Read the id parameter from the URL's query string.
const params = new (location.search);
console.log(params.get("id"));
URLSearchParams parses ?id=...&....What is logged?
const u = new URL("https://a.com/shop?x=1&x=2#top");
console.log(u.pathname, u.searchParams.get("x"), u.hash);Which changes the URL without reloading the page?
Why use IntersectionObserver instead of a scroll listener?
buildSearchUrl(base, term, page) should return a URL string with q and page parameters, correctly encoded. readPage(url) should return the page as a number, defaulting to 1. Fix both.
function buildSearchUrl(base, term, page) {
return base + "?q=" + term + "&page" + page;
}
function readPage(url) {
return new URL(url).searchParams.get("page");
}
console.log(buildSearchUrl("https://a.com/search", "tea & cake", 2));
console.log(readPage("https://a.com/search?page=4"));- Builds an encoded URL
- Reads the page as a number
- Defaults to page 1
JavaScript Browser APIs Quiz
5 questions to check what you learned.
What is location.pathname for https://site.com/blog/post?id=3?
- https://site.com
- /blog/post
- ?id=3
- post
Which event fires when the user presses the back button after pushState?
- load
- hashchange only
- unload
- popstate
What is the global object in the browser?
- document
- navigator
- window
- global
Which API copies text to the clipboard?
- document.copy()
- navigator.clipboard.writeText(text)
- window.clipboard = text
- location.copy(text)
What does threshold: 0.5 mean for an IntersectionObserver?
- Check every half second
- Watch half of the elements
- Use half the screen as the root
- Report when at least half of the element is visible
JavaScript Browser APIs Challenge
Remember a tab in the URL
Make the tabs update the URL and the page:
- Write
showTab(name)that adds classactiveto the button whosedata-tabisname, removes it from the others, and sets#paneltext to`Tab: ${name}` - Clicking a tab button calls
showTabandhistory.replaceState(null, "", "#" + name)(wrap it intry...catchin case the preview blocks it) - On load, read the hash (without
#) fromlocation.hash, or use"intro"if there isn't one, and callshowTabwith it
<div id="tabs">
<button data-tab="intro">Intro</button>
<button data-tab="usage">Usage</button>
<button data-tab="faq">FAQ</button>
</div>
<p id="panel"></p>- Starts on intro
- Clicking switches tabs
- Uses replaceState and location.hash