Skip to content

JavaScript Browser APIs — Geolocation, History, Storage, Media, and Device APIs

DodaTech Updated 2026-06-29 5 min read

In this tutorial, you will learn about JavaScript Browser APIs. We cover key concepts, practical examples, and best practices to help you master this topic.

Modern browsers expose dozens of APIs that let JavaScript interact with the device, the browser, and the user environment. These APIs enable rich web applications that rival native apps: geolocation for maps, media capture for video calls, history for SPA navigation, and more.

DodaTech uses browser APIs throughout the dashboard: History API for navigation, Geolocation for asset mapping, Media Capture for QR code scanning of asset tags, and Storage API for offline capability.

What You'll Learn

  • Geolocation API
  • History API (SPA navigation)
  • Web Storage (localStorage, sessionStorage)
  • Media Capture (camera, microphone)
  • Clipboard API
  • Fullscreen API
  • Screen Orientation API
  • Network Information API
  • Battery API
  • Vibration API

Geolocation API

// Get current position
navigator.geolocation.getCurrentPosition(
  (position) => {
    const { latitude, longitude, accuracy } = position.coords;
    console.log(`Location: ${latitude}, ${longitude}${accuracy}m)`);
  },
  (error) => {
    switch (error.code) {
      case error.PERMISSION_DENIED:
        console.error("User denied location access");
        break;
      case error.POSITION_UNAVAILABLE:
        console.error("Location unavailable");
        break;
      case error.TIMEOUT:
        console.error("Location request timed out");
        break;
    }
  },
  {
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 60000 // Cache 1 minute
  }
);

// Watch position (tracking)
const watchId = navigator.geolocation.watchPosition(
  (position) => {
    updateMap(position.coords.latitude, position.coords.longitude);
  },
  (error) => console.error(error),
  { enableHighAccuracy: true }
);

// Stop watching
navigator.geolocation.clearWatch(watchId);

History API

// SPA navigation without page reload
class Router {
  constructor(routes) {
    this.routes = routes;
    this.currentRoute = null;

    // Handle popstate (back/forward buttons)
    window.addEventListener("popstate", (event) => {
      this.navigateTo(event.state?.path || "/", false);
    });
  }

  navigateTo(path, addToHistory = true) {
    const route = this.matchRoute(path);
    if (!route) {
      console.error(`No route for: ${path}`);
      return;
    }

    this.currentRoute = route;
    this.render(route);

    if (addToHistory) {
      history.pushState({ path }, "", path);
    }
  }

  matchRoute(path) {
    for (const [pattern, handler] of this.routes) {
      const params = this.matchPattern(pattern, path);
      if (params !== null) {
        return { handler, params };
      }
    }
    return null;
  }

  matchPattern(pattern, path) {
    const paramNames = [];
    const regexStr = pattern.replace(/:(\w+)/g, (_, name) => {
      paramNames.push(name);
      return "([^/]+)";
    });
    const regex = new RegExp(`^${regexStr}$`);
    const match = path.match(regex);
    if (!match) return null;

    const params = {};
    paramNames.forEach((name, i) => {
      params[name] = match[i + 1];
    });
    return params;
  }

  render(route) {
    document.getElementById("app").innerHTML = route.handler(route.params);
  }

  replace(path) {
    history.replaceState({ path }, "", path);
    this.navigateTo(path, false);
  }
}

// Usage
const router = new Router([
  ["/", () => "<h1>Home</h1>"],
  ["/users", () => "<h1>Users</h1>"],
  ["/users/:id", (params) => `<h1>User ${params.id}</h1>`],
  ["/settings", () => "<h1>Settings</h1>"],
]);

// Navigation
document.querySelectorAll("a[data-nav]").forEach((link) => {
  link.addEventListener("click", (e) => {
    e.preventDefault();
    router.navigateTo(link.getAttribute("href"));
  });
});

Web Storage

// localStorage (persists across sessions)
localStorage.setItem("theme", "dark");
console.log(localStorage.getItem("theme")); // "dark"
localStorage.removeItem("theme");
localStorage.clear();

// sessionStorage (cleared when tab closes)
sessionStorage.setItem("draft", JSON.stringify({ title: "Draft" }));
const draft = JSON.parse(sessionStorage.getItem("draft"));

// Storage event (cross-tab communication)
window.addEventListener("storage", (event) => {
  console.log(`${event.key} changed:`, event.newValue);
  // Only fires in OTHER tabs, not current tab
});

// Safe storage wrapper
class SafeStorage {
  constructor(storage = localStorage) {
    this.storage = storage;
  }

  get(key, defaultValue = null) {
    try {
      const value = this.storage.getItem(key);
      return value !== null ? JSON.parse(value) : defaultValue;
    } catch {
      return defaultValue;
    }
  }

  set(key, value) {
    try {
      this.storage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      // Quota exceeded or storage disabled
      console.error("Storage failed:", e);
      return false;
    }
  }

  remove(key) {
    this.storage.removeItem(key);
  }

  clear() {
    this.storage.clear();
  }

  get usedSpace() {
    let total = 0;
    for (let i = 0; i < this.storage.length; i++) {
      const key = this.storage.key(i);
      total += (key?.length || 0) + (this.storage.getItem(key)?.length || 0);
    }
    return total;
  }
}

Media Capture

// Camera and microphone
async function startCamera() {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: { width: { ideal: 1280 }, height: { ideal: 720 } },
      audio: true
    });

    const video = document.querySelector("#camera-preview");
    video.srcObject = stream;
    video.play();

    return stream;
  } catch (err) {
    if (err.name === "NotAllowedError") {
      console.error("Camera permission denied");
    } else if (err.name === "NotFoundError") {
      console.error("No camera found");
    } else {
      console.error("Camera error:", err);
    }
  }
}

// Screen capture
async function startScreenShare() {
  try {
    const stream = await navigator.mediaDevices.getDisplayMedia({
      video: true,
      audio: true
    });
    return stream;
  } catch (err) {
    console.error("Screen share cancelled or failed");
  }
}

// Take photo from camera
function capturePhoto(videoElement) {
  const canvas = document.createElement("canvas");
  canvas.width = videoElement.videoWidth;
  canvas.height = videoElement.videoHeight;
  canvas.getContext("2d").drawImage(videoElement, 0, 0);
  return canvas.toDataURL("image/jpeg", 0.8);
}

Clipboard API

// Write to clipboard
async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log("Copied!");
  } catch (err) {
    // Fallback for older browsers
    const textarea = document.createElement("textarea");
    textarea.value = text;
    document.body.appendChild(textarea);
    textarea.select();
    document.execCommand("copy");
    document.body.removeChild(textarea);
  }
}

// Read from clipboard
async function pasteFromClipboard() {
  try {
    const text = await navigator.clipboard.readText();
    return text;
  } catch (err) {
    console.error("Failed to read clipboard:", err);
  }
}

// Copy image
async function copyImageToClipboard(canvas) {
  try {
    const blob = await new Promise(resolve => canvas.toBlob(resolve));
    await navigator.clipboard.write([
      new ClipboardItem({ "image/png": blob })
    ]);
  } catch (err) {
    console.error("Failed to copy image:", err);
  }
}

Other Useful APIs

// Fullscreen
function toggleFullscreen() {
  if (!document.fullscreenElement) {
    document.documentElement.requestFullscreen();
  } else {
    document.exitFullscreen();
  }
}

// Screen Orientation
function lockOrientation() {
  screen.orientation.lock("portrait").catch(() => {});
}

screen.orientation.addEventListener("change", () => {
  console.log("Orientation:", screen.orientation.type);
});

// Network Information
const connection = navigator.connection || navigator.mozConnection;
if (connection) {
  console.log("Connection type:", connection.effectiveType); // "4g", "3g", "2g", "slow-2g"
  console.log("Downlink:", connection.downlink, "Mbps");
  console.log("RTT:", connection.rtt, "ms");

  connection.addEventListener("change", () => {
    console.log("Network changed:", connection.effectiveType);
  });
}

// Battery
navigator.getBattery?.().then((battery) => {
  console.log("Battery level:", battery.level * 100, "%");
  console.log("Charging:", battery.charging);

  battery.addEventListener("levelchange", () => {
    if (battery.level < 0.2) {
      console.warn("Battery low!");
    }
  });
});

// Vibration
navigator.vibrate?.(200); // Vibrate for 200ms
navigator.vibrate?.([100, 50, 100, 50, 200]); // Pattern: vibrate-pause-vibrate...

Practice Questions

  1. Build a geolocation-based "nearby" feature that shows distance from user's location.

  2. Implement an SPA router using History API with nested routes and URL params.

  3. Create a media recorder that captures video from the camera and saves it as a blob.

  4. Build a "copy to clipboard" button with visual feedback and fallback for old browsers.

  5. Implement a network-aware image loader that loads low-res images on slow connections.

Challenge: Progressive Web App Shell

Build a PWA shell using browser APIs:

  • Service Worker for offline Caching
  • localStorage for user preferences
  • History API for navigation
  • Network Information API for adaptive loading
  • Fullscreen for immersive mode
  • Vibration for notifications
  • Media Capture for profile photos

This is the foundation that DodaTech's dashboard PWA is built on — providing a native-app-like experience for security teams monitoring scans on the go.

Real-World Task: Offline-Enabled Data Collection App

Build a browser app for field data collection that works offline:

  • Capture photos (Media Capture)
  • Record GPS location (Geolocation)
  • Fill forms and save drafts (IndexedDB/localStorage)
  • Queue submissions when offline (Network Information)
  • Auto-sync when connection restores
  • Handle conflicts (server vs local changes)
  • Show sync status indicator

This matches DodaTech's mobile field agent for physical security audits — technicians walk through facilities, scan QR codes on assets, take photos, and record observations, all while potentially offline.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro