Topic 11.8
More Browser APIs
In one line
Powerful browser features such as the clipboard, the user's location and system notifications are guarded by permissions: they need a secure (HTTPS) page, often a user action, and the user's consent, so good code detects support, asks at the right moment and has a fallback when the answer is no.
Think of it like this
Asking to borrow a neighbour's car. You don't take the keys from their hook without asking. You ask, at a sensible moment, and say why ("can I borrow it to take my mum to the doctor?"). They might say yes, say no, or say "never ask me again". And if they say no, you take the bus. Browser permissions work the same way: the page asks, the user decides, and your app needs a plan B.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Permission
- The user's yes or no to a powerful feature such as location or notifications, remembered per site.
- Secure context
- A page served over HTTPS (or from
localhost). Most powerful APIs only work there. - User activation
- A real click, tap or key press by the user. Some APIs only work right after one.
- Permissions Policy
- Rules (an HTTP header or an iframe's
allowattribute) that say which features a page or frame may use at all. - Clipboard
- The system's copy-and-paste memory, shared with other apps.
- Geolocation
- The device's position on Earth: latitude, longitude and how accurate it is.
- Notification
- A message the operating system shows outside the browser window.
- Feature detection
- Checking that an API exists before using it, like
if ("share" in navigator). - NotAllowedError
- The error name most permission-guarded APIs reject with when the user or browser says no.
Step by step
01Detect the feature
Never assume an API exists: older browsers, insecure pages and some embeds don't have it.
const canCopy = window.isSecureContext && "clipboard" in navigator;
const canLocate = "geolocation" in navigator;
const canNotify = "Notification" in window;
const canShare = typeof navigator.share === "function";02Copy on click, with a fallback
Call the API inside the click handler, where the user activation is fresh. If it fails, select the text so the user can copy it themselves.
copyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(codeInput.value);
status.textContent = "Copied!";
} catch {
codeInput.select();
status.textContent = "Press Ctrl+C (Cmd+C on Mac) to copy.";
}
});03Turn geolocation into a promise
A small wrapper lets the rest of your code use await and try/catch.
function getPosition(options) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
const pos = await getPosition({ timeout: 10000, maximumAge: 60000 });
console.log(pos.coords.latitude, pos.coords.longitude, pos.coords.accuracy);04Check first, ask at the right time
The permission state decides what to show. Only the button click triggers the real browser prompt.
05Ask for notifications from a button
Requesting on page load is the classic mistake: most users block it, and browsers now hide such prompts.
enableButton.addEventListener("click", async () => {
if (!("Notification" in window)) return;
const result = await Notification.requestPermission();
if (result === "granted") {
new Notification("Reminders on", { body: "We will remind you before class starts." });
}
});06Why the Hectal preview says no
Hectal's live previews run in a sandboxed frame without these permissions, so real clipboard, location and notification calls fail there. The live examples below use small fake versions of the APIs to show the logic; try the real calls on your own HTTPS page or localhost.
Try it yourself
- 1
Add a timeout fake
In the first example, add a fake that fails with
code: 2and callwhereAmIwith it. Predict the message first. - 2
Try the real thing on a real page
On any HTTPS site, open the console and run
navigator.permissions.query({ name: "geolocation" }).then((s) => console.log(s.state)). Then runnavigator.geolocation.getCurrentPosition((p) => console.log(p.coords), (e) => console.log(e.code, e.message)), answer the prompt, and run the first line again. Reset it from the padlock icon in the address bar. - 3
Watch the copy fallback
Press Copy in the third example and read the message. Predict: what would it say on an
http://(not HTTPS) page in a normal tab?
Code & diagrams
Swap a fake for `navigator.geolocation` on your own HTTPS page and the same code asks the browser for real.
Preview
Console
Nothing logged yet.Expected console output
You are near 18.52, 73.86 (within 30 m)
You blocked location. Type your city instead.
Finding your position took too long. Try again.On a real page: `const status = await navigator.permissions.query({ name: "geolocation" })`, then `uiFor(status.state)`, and `status.onchange = () => render(uiFor(status.state))` to follow changes made in site settings.
Preview
Console
Nothing logged yet.Expected console output
granted: (no button)
prompt: Use my location
denied: Type a city
unsupported: Type a cityPress Copy. In Hectal's sandboxed preview the clipboard isn't allowed, so you'll see the fallback: the code gets selected and the message explains what to do. On a normal HTTPS page the same button copies.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Ask for notifications on page load
On a real page, put Notification.requestPermission() at the top of your script so it runs on load.
Break #2
Read the clipboard without asking
Call await navigator.clipboard.readText() in a setTimeout on page load.
Myth vs fact
Myth
If the user says no once, you can ask again later.
Fact
After the user blocks a permission, calls fail immediately without a prompt. Only the user can change it, in site settings. Ask once, at the right time.
Myth
Geolocation always uses GPS and is exact.
Fact
Browsers combine Wi-Fi, cell towers, IP address and GPS. On a laptop, accuracy may be hundreds of metres; always read coords.accuracy.
Myth
The Permissions API can request permissions.
Fact
navigator.permissions.query() only reports the state. Requesting happens by calling the feature itself (getCurrentPosition, requestPermission, readText).
Myth
These APIs work on any page.
Fact
Most need HTTPS (or localhost), many need a user gesture, and in iframes the parent must allow them with Permissions Policy.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Sites can disable features for themselves and all embeds with the
Permissions-Policyheader (for examplePermissions-Policy: geolocation=(), camera=()), which limits what a compromised third-party script could do. - ▸
navigator.userActivation.isActiveandhasBeenActiveexpose transient and sticky user activation, the same flags browsers check before allowing popups, fullscreen, clipboard writes and audio autoplay. - ▸
Geolocation precision is a privacy issue: if you only need the city, round coordinates before sending them to your server, or use an IP-based lookup server-side and skip the prompt altogether.
- ▸
Background notifications need a service worker plus the Push API with VAPID keys; on iOS, web push works only for sites added to the Home Screen (since iOS 16.4).
Remember this
- 1
Many browser features could hurt users if any page used them silently: reading what you copied (maybe a password), knowing where you are, popping up messages outside the browser, using the camera. So browsers protect them in layers. First, a secure context: the page must be served over HTTPS (or be
localhost); check withwindow.isSecureContext. Second, often user activation: the call must happen during or shortly after a real click or key press, not on page load. Third, a permission: the browser asks the user, remembers the answer per origin, and lets the user change it in the site settings. Inside iframes, the parent page must also allow the feature with Permissions Policy (<iframe allow="clipboard-write; geolocation">). - 2
The Clipboard API lives at
navigator.clipboard.await navigator.clipboard.writeText(text)copies text; it's allowed after a click on a focused page (no prompt).await navigator.clipboard.readText()reads it, and browsers guard this more strongly: Chrome asks for permission, Safari and Firefox show a small "Paste" button the user must press. Both return promises that reject (usually withNotAllowedError) when not allowed.write()andread()withClipboardItemhandle images and HTML. The olddocument.execCommand("copy")is deprecated; keep it only as a fallback. - 3
The Geolocation API (
navigator.geolocation) gives the device's position. It uses callbacks, not promises:getCurrentPosition(onSuccess, onError, options). Success receives a position withcoords.latitude,coords.longitudeandcoords.accuracy(in metres). Errors have acode:1permission denied,2position unavailable,3timeout. Options:enableHighAccuracy(GPS, slower and more battery),timeout(ms) andmaximumAge(accept a cached position this old).watchPositionkeeps reporting as the user moves untilclearWatch(id). Wrapping the callbacks in aPromise(Phase 10) makes it easy toawait. - 4
The Notifications API shows system notifications.
Notification.permissionis"default"(not asked yet),"granted"or"denied".await Notification.requestPermission()asks; browsers ignore or quietly block requests not triggered by a user action, and a user who clicks Block can only undo it in site settings, so you get roughly one chance. Thennew Notification("Title", { body: "Text" })shows one while the page is open. Notifications when the site is closed need a service worker and the Push API (and on Android,new Notification()doesn't work at all; useregistration.showNotification()). - 5
The Permissions API lets you check the current state without asking:
const status = await navigator.permissions.query({ name: "geolocation" })givesstatus.stateas"granted","prompt"or"denied", andstatus.onchangefires when the user changes it. It can't request anything, and the supported names differ between browsers (wrap it intry/catch). Use it to choose the UI: show the map straight away if granted, show an explanation and an "Use my location" button ifprompt, and show how to re-enable it (or a manual input) if denied. - 6
The pattern for every permission-based API: (1) feature-detect (
if ("geolocation" in navigator)), (2) explain first, ask later: show your own message and a button, and call the API from that button's click, (3) handle every answer, including errors and timeouts, and (4) always offer a fallback: a text box for the city, a visible text field to copy from, an in-page message instead of a notification. Other useful APIs follow the same rules: Web Share (navigator.share({ title, url }), the phone's share sheet), Page Visibility (document.visibilityStateandvisibilitychange, to pause work in hidden tabs), and Screen Wake Lock (keep the screen on for a recipe).
Explain it without notes
What layers of protection do browsers put around powerful APIs?
How do you use the Geolocation API with async/await, and what are its error codes?
What is the right way to ask for notification permission?
What can and can't the Permissions API do?
Practice
Write supports() that returns an object saying whether clipboard, geolocation, notifications and share are available, then log it.
Write copyText(text) that resolves to "copied" or "manual" (when the Clipboard API is missing or rejects), never throwing.
Trade-offs
- ↔
Asking for a permission unlocks a smoother experience (automatic location, timely reminders) but costs trust and can be refused forever; asking later, with context, gets more yeses than asking early.
- ↔
Browser APIs avoid installing an app, but support and behaviour differ by browser and platform, so each one needs feature detection, testing on real devices and a fallback.
Done when you can
I can explain secure contexts, user activation, Permissions Policy and permission prompts.
I can copy text with the Clipboard API and fall back gracefully.
I can get the user's position with a promise wrapper and handle error codes 1, 2 and 3.
I ask for notification permission only from a user action.
I can check a permission's state with
navigator.permissions.query.