Command Palette

Search for a command to run...

Hectal
PHASE 11Intermediate ~24 min· topic 8 of 8

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 allow attribute) 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.

detect.jswhole filejavascript
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.

copy.jswhole filejavascript
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.

where.jswhole filejavascript
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.

Check first, ask at the right timediagram
Rendering diagram…

05Ask for notifications from a button

Requesting on page load is the classic mistake: most users block it, and browsers now hide such prompts.

notify.jswhole filejavascript
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. 1

    Add a timeout fake

    In the first example, add a fake that fails with code: 2 and call whereAmI with it. Predict the message first.

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

Wrap geolocation in a promise and handle every error code ES2020+ New tab

Swap a fake for `navigator.geolocation` on your own HTTPS page and the same code asks the browser for real.

Preview

Press Run to see the page.

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.
Choose the UI from the permission state New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

granted: (no button)
prompt: Use my location
denied: Type a city
unsupported: Type a city
A copy button that always ends well New tab

Press 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

Press Run to see the page.

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.

terminal
$ Browser
── what you'll see ──
Chrome shows only a small "Notifications blocked" icon in the address bar (quiet UI) or nothing; Firefox and Safari refuse requests not triggered by a user gesture; users who do see the prompt mostly click Block, and you can't ask them again.

Break #2

Read the clipboard without asking

Call await navigator.clipboard.readText() in a setTimeout on page load.

terminal
$ Console
── what you'll see ──
Uncaught (in promise) NotAllowedError: Failed to execute 'readText' on 'Clipboard': ...

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-Policy header (for example Permissions-Policy: geolocation=(), camera=()), which limits what a compromised third-party script could do.

  • ▸

    navigator.userActivation.isActive and hasBeenActive expose 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. 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 with window.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. 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 with NotAllowedError) when not allowed. write() and read() with ClipboardItem handle images and HTML. The old document.execCommand("copy") is deprecated; keep it only as a fallback.

  3. 3

    The Geolocation API (navigator.geolocation) gives the device's position. It uses callbacks, not promises: getCurrentPosition(onSuccess, onError, options). Success receives a position with coords.latitude, coords.longitude and coords.accuracy (in metres). Errors have a code: 1 permission denied, 2 position unavailable, 3 timeout. Options: enableHighAccuracy (GPS, slower and more battery), timeout (ms) and maximumAge (accept a cached position this old). watchPosition keeps reporting as the user moves until clearWatch(id). Wrapping the callbacks in a Promise (Phase 10) makes it easy to await.

  4. 4

    The Notifications API shows system notifications. Notification.permission is "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. Then new 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; use registration.showNotification()).

  5. 5

    The Permissions API lets you check the current state without asking: const status = await navigator.permissions.query({ name: "geolocation" }) gives status.state as "granted", "prompt" or "denied", and status.onchange fires when the user changes it. It can't request anything, and the supported names differ between browsers (wrap it in try/catch). Use it to choose the UI: show the map straight away if granted, show an explanation and an "Use my location" button if prompt, and show how to re-enable it (or a manual input) if denied.

  6. 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.visibilityState and visibilitychange, to pause work in hidden tabs), and Screen Wake Lock (keep the screen on for a recipe).

Explain it without notes

01

What layers of protection do browsers put around powerful APIs?

02

How do you use the Geolocation API with async/await, and what are its error codes?

03

What is the right way to ask for notification permission?

04

What can and can't the Permissions API do?

Practice

01

Write supports() that returns an object saying whether clipboard, geolocation, notifications and share are available, then log it.

02

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.