Welcome back! 👋 In the previous lesson, you mastered the history object. Now let's explore the navigator object — the window into everything the browser knows about itself and the user's device!
The navigator object gives you access to browser type, language settings, online/offline status, the user's physical location, clipboard access, installed plugins, and much more. You'll use it to build adaptive, device-aware experiences — detecting when users go offline, requesting their location, or accessing their camera and microphone.
Let's master it completely!
What is the Navigator Object?
The navigator object (window.navigator) provides information about the browser and the device it's running on. Unlike window (which controls the browser window) or document (which controls the page content), navigator tells you who and what is accessing your page.
console.log(window.navigator); // Navigator object
console.log(navigator); // Same — window is implicit
console.log(navigator.userAgent); // Browser identification string
console.log(navigator.language); // User's preferred language
console.log(navigator.onLine); // Is the user online?Browser Information
navigator.userAgent — Browser Identity String
The most well-known navigator property — a string that identifies the browser, its version, and the OS.
console.log(navigator.userAgent);
// Chrome on Windows:
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
// Chrome on Android:
// "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36"
// Safari on iPhone:
// "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"Detect Browser Type
function getBrowserInfo() {
let ua = navigator.userAgent;
let browser =
ua.includes("Edg") ? "Microsoft Edge" :
ua.includes("OPR") ? "Opera" :
ua.includes("Chrome") ? "Chrome" :
ua.includes("Safari") ? "Safari" :
ua.includes("Firefox") ? "Firefox" :
"Unknown Browser";
let isDesktop = !ua.includes("Mobile") && !ua.includes("Android");
let device = isDesktop ? "Desktop" : "Mobile";
return { browser, device, ua };
}
let info = getBrowserInfo();
console.log(`Browser: ${info.browser}`);
console.log(`Device: ${info.device}`);navigator.vendor — Browser Vendor
console.log(navigator.vendor);
// Chrome: "Google Inc."
// Safari: "Apple Computer, Inc."
// Firefox: "" (empty string)navigator.appVersion — Browser Version Info
console.log(navigator.appVersion);
// "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."Language and Locale
navigator.language — User's Primary Language
console.log(navigator.language);
// "en-IN" (English, India)
// "hi-IN" (Hindi, India)
// "en-US" (English, US)
// "fr-FR" (French, France)navigator.languages — All Preferred Languages (in order)
console.log(navigator.languages);
// ["en-IN", "en-GB", "en", "hi"] — user's language preferences in orderPractical — Detect and Apply Language
function getUserLocale() {
let lang = navigator.language || navigator.languages[0] || "en";
let [language, region] = lang.split("-");
return {
full: lang,
language: language.toLowerCase(),
region: region?.toUpperCase() ?? "US",
isRTL: ["ar", "he", "fa", "ur"].includes(language.toLowerCase()),
};
}
let locale = getUserLocale();
console.log(`Language: ${locale.language}`);
console.log(`Region: ${locale.region}`);
console.log(`RTL: ${locale.isRTL}`);
// Use locale for formatting
let amount = 1234567.89;
console.log(amount.toLocaleString(locale.full, {
style: "currency",
currency: locale.region === "IN" ? "INR" : "USD",
}));
// e.g. "₹12,34,567.89" for en-INOnline / Offline Status
navigator.onLine — Is the User Online?
console.log(navigator.onLine); // true or falseListen for Online/Offline Events
window.addEventListener("online", () => {
console.log("🟢 Back online!");
// Sync pending data, retry failed requests
});
window.addEventListener("offline", () => {
console.log("🔴 Gone offline!");
// Show offline banner, queue requests
});Practical — Offline-Aware App
class ConnectionManager {
constructor() {
this.isOnline = navigator.onLine;
this.queue = []; // Pending requests when offline
this.listeners = [];
window.addEventListener("online", () => this.handleOnline());
window.addEventListener("offline", () => this.handleOffline());
}
handleOnline() {
this.isOnline = true;
console.log("🟢 Connection restored");
this.notify("online");
this.flushQueue();
}
handleOffline() {
this.isOnline = false;
console.log("🔴 Connection lost");
this.notify("offline");
}
notify(status) {
this.listeners.forEach(fn => fn(status));
}
onChange(fn) {
this.listeners.push(fn);
}
enqueue(request) {
if (this.isOnline) {
return request(); // Execute immediately
}
this.queue.push(request);
console.log(`📋 Queued (${this.queue.length} pending)`);
}
flushQueue() {
console.log(`🔄 Flushing ${this.queue.length} queued requests`);
while (this.queue.length) {
this.queue.shift()();
}
}
}
let conn = new ConnectionManager();
conn.onChange(status => {
let banner = document.getElementById("offlineBanner");
if (banner) {
banner.style.display = status === "offline" ? "block" : "none";
}
});
// Queue a request when offline
conn.enqueue(() => console.log("📤 Sending form data..."));Geolocation
The Geolocation API lets you request the user's physical location — with their explicit permission.
navigator.geolocation.getCurrentPosition() — One-Time Location
function getLocation() {
if (!navigator.geolocation) {
console.log("❌ Geolocation not supported by this browser");
return;
}
navigator.geolocation.getCurrentPosition(
// Success callback
(position) => {
let { latitude, longitude, accuracy, altitude, speed } = position.coords;
let timestamp = new Date(position.timestamp);
console.log(`📍 Latitude: ${latitude}`);
console.log(`📍 Longitude: ${longitude}`);
console.log(`📍 Accuracy: ${accuracy} meters`);
console.log(`⏱️ Time: ${timestamp.toLocaleTimeString()}`);
},
// Error callback
(error) => {
let messages = {
1: "Permission denied — user blocked location access",
2: "Position unavailable — location service failed",
3: "Timeout — took too long to get location",
};
console.log(`❌ Error: ${messages[error.code]}`);
},
// Options
{
enableHighAccuracy: true, // Use GPS if available
timeout: 10000, // Give up after 10 seconds
maximumAge: 0, // Don't use cached position
}
);
}
getLocation();navigator.geolocation.watchPosition() — Track Live Location
let watchId = navigator.geolocation.watchPosition(
(position) => {
let { latitude, longitude } = position.coords;
console.log(`📍 Updated position: ${latitude}, ${longitude}`);
// Update map marker in real time
},
(error) => {
console.error("Location error:", error.message);
},
{ enableHighAccuracy: true, timeout: 5000 }
);
// Stop tracking
function stopTracking() {
navigator.geolocation.clearWatch(watchId);
console.log("🛑 Location tracking stopped");
}
// Stop after 30 seconds
setTimeout(stopTracking, 30000);Practical — Nearest Store Finder
function findNearestStore(userLat, userLon, stores) {
function distanceKm(lat1, lon1, lat2, lon2) {
let R = 6371; // Earth radius in km
let dLat = (lat2 - lat1) * Math.PI / 180;
let dLon = (lon2 - lon1) * Math.PI / 180;
let a =
Math.sin(dLat/2) ** 2 +
Math.cos(lat1 * Math.PI / 180) *
Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
return stores
.map(store => ({
...store,
distanceKm: distanceKm(userLat, userLon, store.lat, store.lon).toFixed(2),
}))
.sort((a, b) => a.distanceKm - b.distanceKm);
}
let stores = [
{ name: "Andheri Store", lat: 19.1197, lon: 72.8464 },
{ name: "Bandra Store", lat: 19.0596, lon: 72.8295 },
{ name: "Dadar Store", lat: 19.0178, lon: 72.8478 },
];
navigator.geolocation.getCurrentPosition(({ coords }) => {
let sorted = findNearestStore(coords.latitude, coords.longitude, stores);
console.log("🏪 Nearest stores:");
sorted.forEach(s => console.log(` ${s.name}: ${s.distanceKm} km away`));
});Platform and Device Info
navigator.platform — Operating System
console.log(navigator.platform);
// "Win32" — Windows
// "MacIntel" — macOS
// "Linux x86_64" — Linux
// "iPhone" — iOS
// "Android" — Android (some browsers)navigator.hardwareConcurrency — CPU Cores
console.log(navigator.hardwareConcurrency);
// e.g. 8 — number of logical CPU cores
// Useful for deciding how many Web Workers to spawnnavigator.deviceMemory — Device RAM (GB)
console.log(navigator.deviceMemory);
// e.g. 8 — 8GB RAM
// Values are rounded: 0.25, 0.5, 1, 2, 4, 8
// Useful for adapting to low-memory devicesnavigator.maxTouchPoints — Is it a Touch Device?
console.log(navigator.maxTouchPoints);
// 0 — no touch support (desktop)
// 1 — basic touch (some laptops)
// 5+ — multi-touch (phones, tablets)
let isTouchDevice = navigator.maxTouchPoints > 0;
console.log(`Touch device: ${isTouchDevice}`);Practical — Device Capability Detector
function detectDeviceCapabilities() {
return {
cores: navigator.hardwareConcurrency ?? "unknown",
ram: navigator.deviceMemory ?? "unknown",
touch: navigator.maxTouchPoints > 0,
touchPoints: navigator.maxTouchPoints,
online: navigator.onLine,
language: navigator.language,
platform: navigator.platform,
};
}
let caps = detectDeviceCapabilities();
console.log("=== 📱 Device Capabilities ===");
for (let [key, val] of Object.entries(caps)) {
console.log(` ${key.padEnd(12)}: ${val}`);
}
// Adapt experience based on capabilities
if (caps.ram <= 2) {
console.log("⚠️ Low RAM device — using lightweight mode");
}
if (caps.touch) {
console.log("📱 Touch device — enabling swipe gestures");
}
if (!caps.online) {
console.log("🔴 Offline — enabling offline mode");
}Clipboard API
navigator.clipboard.writeText() — Copy to Clipboard
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log(`✅ Copied: "${text}"`);
} catch (err) {
console.error("❌ Copy failed:", err.message);
}
}
copyToClipboard("Hello, World!");
// Common pattern — copy button
document.getElementById("copyBtn")?.addEventListener("click", async () => {
let code = document.getElementById("codeBlock").textContent;
await copyToClipboard(code);
let btn = document.getElementById("copyBtn");
btn.textContent = "✅ Copied!";
setTimeout(() => btn.textContent = "Copy", 2000);
});navigator.clipboard.readText() — Paste from Clipboard
async function pasteFromClipboard() {
try {
let text = await navigator.clipboard.readText();
console.log(`📋 Pasted: "${text}"`);
return text;
} catch (err) {
console.error("❌ Paste failed:", err.message);
return null;
}
}Share API
navigator.share() — Native Share Dialog
Opens the device's native share sheet — works on mobile browsers.
async function shareContent(data) {
if (!navigator.share) {
console.log("❌ Web Share API not supported");
// Fallback: copy URL to clipboard
await navigator.clipboard.writeText(data.url ?? window.location.href);
return;
}
try {
await navigator.share({
title: data.title ?? document.title,
text: data.text ?? "",
url: data.url ?? window.location.href,
});
console.log("✅ Shared successfully");
} catch (err) {
if (err.name !== "AbortError") {
console.error("Share failed:", err.message);
}
}
}
document.getElementById("shareBtn")?.addEventListener("click", () => {
shareContent({
title: "Learn JavaScript — DOM & BOM",
text: "Check out this amazing JavaScript tutorial!",
url: window.location.href,
});
});Real-World Examples
Example 1: Complete Device Info Panel
function buildDeviceReport() {
let ua = navigator.userAgent;
let isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i.test(ua);
let report = {
"Browser": getBrowserName(ua),
"Platform": navigator.platform,
"Language": navigator.language,
"Online": navigator.onLine ? "✅ Yes" : "❌ No",
"Touch": navigator.maxTouchPoints > 0 ? `✅ Yes (${navigator.maxTouchPoints} points)` : "❌ No",
"CPU Cores": navigator.hardwareConcurrency ?? "Unknown",
"RAM": navigator.deviceMemory ? `${navigator.deviceMemory} GB` : "Unknown",
"Device Type": isMobile ? "📱 Mobile" : "🖥️ Desktop",
"Cookies": navigator.cookieEnabled ? "✅ Enabled" : "❌ Disabled",
};
console.log("=== 📊 Device Report ===");
for (let [key, val] of Object.entries(report)) {
console.log(` ${key.padEnd(14)}: ${val}`);
}
return report;
}
function getBrowserName(ua) {
if (ua.includes("Edg")) return "Microsoft Edge";
if (ua.includes("OPR")) return "Opera";
if (ua.includes("Chrome")) return "Chrome";
if (ua.includes("Safari")) return "Safari";
if (ua.includes("Firefox")) return "Firefox";
return "Unknown";
}
buildDeviceReport();Example 2: Location-Based Greeting
async function personalizeGreeting() {
let greeting = "Hello";
let langMap = {
"hi": "नमस्ते",
"fr": "Bonjour",
"de": "Hallo",
"es": "Hola",
"ja": "こんにちは",
"ar": "مرحبا",
};
let lang = navigator.language.split("-")[0].toLowerCase();
greeting = langMap[lang] ?? "Hello";
let timeStr = new Date().toLocaleTimeString(navigator.language);
let dateStr = new Date().toLocaleDateString(navigator.language, {
weekday: "long", day: "numeric", month: "long",
});
console.log(`${greeting}! 👋`);
console.log(`📅 ${dateStr}`);
console.log(`⏰ ${timeStr}`);
console.log(`🌐 Your language: ${navigator.language}`);
console.log(`🔗 Status: ${navigator.onLine ? "Online" : "Offline"}`);
}
personalizeGreeting();Example 3: Progressive Feature Detection
function checkFeatureSupport() {
let features = {
geolocation: !!navigator.geolocation,
clipboard: !!navigator.clipboard,
share: !!navigator.share,
bluetooth: !!navigator.bluetooth,
usb: !!navigator.usb,
vibration: !!navigator.vibrate,
notifications: "Notification" in window,
serviceWorker: "serviceWorker" in navigator,
webRTC: !!window.RTCPeerConnection,
localStorage: (() => {
try { localStorage.setItem("t", "1"); localStorage.removeItem("t"); return true; }
catch { return false; }
})(),
};
console.log("=== 🔍 Feature Support ===");
for (let [feature, supported] of Object.entries(features)) {
console.log(` ${feature.padEnd(18)}: ${supported ? "✅ Supported" : "❌ Not supported"}`);
}
return features;
}
let features = checkFeatureSupport();
// Conditionally enable features
if (features.geolocation) {
document.getElementById("locationBtn")?.removeAttribute("disabled");
}
if (!features.serviceWorker) {
console.log("⚠️ No service worker — offline mode unavailable");
}Common Mistakes
Mistake 1: Relying on userAgent for Feature Detection
// ❌ userAgent sniffing is fragile — browsers lie, strings change!
if (navigator.userAgent.includes("Chrome")) {
enableChromeFeature(); // May break with Chrome updates ❌
}
// ✅ Use feature detection instead — check if the API exists
if ("clipboard" in navigator) {
enableClipboardFeature(); // Works regardless of browser ✅
}
if (navigator.geolocation) {
enableLocationFeature(); // Safe check ✅
}Mistake 2: Not Handling Geolocation Permission Denial
// ❌ No error handling — crashes when user denies permission
navigator.geolocation.getCurrentPosition(pos => {
showMap(pos.coords);
});
// ✅ Always handle all error cases
navigator.geolocation.getCurrentPosition(
pos => showMap(pos.coords),
err => {
if (err.code === 1) console.log("Permission denied — show manual input");
if (err.code === 2) console.log("Position unavailable — try again");
if (err.code === 3) console.log("Timeout — check connection");
showManualLocationInput(); // Fallback ✅
}
);Mistake 3: Assuming navigator.onLine Means Full Connectivity
// ⚠️ navigator.onLine only means a network interface is connected
// It does NOT mean the internet is actually reachable!
if (navigator.onLine) {
fetchData(); // Could still fail if wifi has no internet! ⚠️
}
// ✅ Always handle fetch errors too — even when "online"
if (navigator.onLine) {
try {
let data = await fetch("/api/data");
return await data.json();
} catch (err) {
console.log("Fetch failed despite being 'online':", err.message);
return fallbackData;
}
}Mistake 4: Not Checking Clipboard API Support
// ❌ Clipboard API requires HTTPS and user permission — not always available
await navigator.clipboard.writeText(text); // Throws on HTTP! ❌
// ✅ Always check support and handle errors
async function safeCopy(text) {
if (!navigator.clipboard) {
// Fallback for older browsers
let el = document.createElement("textarea");
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
return;
}
try {
await navigator.clipboard.writeText(text);
console.log("✅ Copied!");
} catch (err) {
console.error("❌ Copy failed:", err.message);
}
}Navigator Properties Cheat Sheet
| Property / Method | What it does |
|---|---|
navigator.userAgent | Browser identification string |
navigator.language | User's primary language (e.g. "en-IN") |
navigator.languages | Array of preferred languages |
navigator.onLine | true if online, false if offline |
navigator.platform | OS/platform string |
navigator.vendor | Browser vendor name |
navigator.hardwareConcurrency | Number of logical CPU cores |
navigator.deviceMemory | Device RAM in GB (rounded) |
navigator.maxTouchPoints | Max simultaneous touch points |
navigator.cookieEnabled | Are cookies enabled? |
navigator.geolocation | Geolocation API object |
geolocation.getCurrentPosition() | Get user's location once |
geolocation.watchPosition() | Track location continuously |
geolocation.clearWatch(id) | Stop tracking location |
navigator.clipboard.writeText() | Copy text to clipboard |
navigator.clipboard.readText() | Paste text from clipboard |
navigator.share() | Open native share dialog |
"serviceWorker" in navigator | Check service worker support |
Key Takeaways
Congratulations! 🎉 You now fully understand the navigator object and all the browser and device information it exposes.
✅ navigator.userAgent — identifies the browser. Use for information only — not for feature detection.
✅ navigator.language / navigator.languages — user's locale. Use for formatting dates, numbers, and currency correctly.
✅ navigator.onLine — quick online check. Always handle fetch errors too — onLine doesn't guarantee internet.
✅ Geolocation — getCurrentPosition() for one-time, watchPosition() for live tracking. Always handle permission denial gracefully.
✅ Device info — hardwareConcurrency, deviceMemory, maxTouchPoints for adaptive experiences.
✅ Clipboard — clipboard.writeText() / readText() — requires HTTPS and user permission. Always fall back gracefully.
✅ navigator.share() — native share sheet on mobile. Check support before calling.
✅ Feature detection over UA sniffing — always check "feature" in navigator rather than parsing the user agent string.
Best Practices
- ✅ Use feature detection (
"geolocation" in navigator) — never UA sniffing for feature support - ✅ Always handle geolocation permission denial — show a manual input fallback
- ✅ Don't trust
navigator.onLinealone — always catch fetch errors too - ✅ The Clipboard API requires HTTPS — always check support and have a fallback
- ✅ Use
navigator.languagefor locale-aware formatting withtoLocaleString() - ✅ Use
navigator.deviceMemoryandhardwareConcurrencyto adapt for low-end devices - ✅ Check
navigator.sharebefore calling — it's only available on mobile browsers - ✅ Always
clearWatch()when you no longer need live location — saves battery
What's Next?
Great work! 🎉 You now know how to read everything the browser knows about itself and the user's device.
Next up, let's look at the user's screen:
Screen Object — screen width, height, available space, color depth, pixel ratio, and how to use them to build responsive, display-aware experiences!
Let's keep going! 💪