Skip to content

Navigator and Screen Objects: Browser and Screen Information Detectives

Division of Labor Between Two Detectives

In the BOM world, navigator and screen are like two professional information detectives. navigator specializes in collecting browser and system information—telling you "what browser and OS the user is using, whether they're online"; while screen focuses on screen hardware information—telling you "how big the user's screen is, how many colors it supports, what the resolution is."

Understanding the difference between these two objects is important:

  • Navigator: Focuses on browser software environment (browser type, version, plugins, language, etc.)
  • Screen: Focuses on display hardware environment (screen size, color depth, orientation, etc.)

Working together, they help us implement intelligent device adaptation and feature detection.

The navigator object contains various information about the browser and operating system, and is an important tool for feature detection and user environment analysis.

Browser Identification Properties

javascript
// User agent string (most common browser identification method)
console.log(navigator.userAgent);
// e.g.: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'

// Application name (usually 'Netscape', for historical reasons)
console.log(navigator.appName);

// Application version
console.log(navigator.appVersion);

// Operating system platform
console.log(navigator.platform);
// e.g.: 'Win32', 'MacIntel', 'Linux x86_64'

// Browser vendor
console.log(navigator.vendor);
// e.g.: 'Google Inc.', 'Apple Computer, Inc.'

// Browser engine name
console.log(navigator.product);
// Usually 'Gecko'

Important Note: Since userAgent can be spoofed, modern best practice is to use feature detection rather than browser detection.

Practical Application: Browser Detection Utility

javascript
class BrowserDetector {
  static get userAgent() {
    return navigator.userAgent.toLowerCase();
  }

  // Detect browser type
  static getBrowserName() {
    const ua = this.userAgent;

    if (ua.includes("edg/")) return "Edge";
    if (ua.includes("chrome/") && !ua.includes("edg/")) return "Chrome";
    if (ua.includes("safari/") && !ua.includes("chrome/")) return "Safari";
    if (ua.includes("firefox/")) return "Firefox";
    if (ua.includes("opera/") || ua.includes("opr/")) return "Opera";
    if (ua.includes("trident/") || ua.includes("msie "))
      return "Internet Explorer";

    return "Unknown";
  }

  // Detect browser version
  static getBrowserVersion() {
    const ua = this.userAgent;
    const browser = this.getBrowserName();

    let match;

    switch (browser) {
      case "Chrome":
        match = ua.match(/chrome\/([\d.]+)/);
        break;
      case "Safari":
        match = ua.match(/version\/([\d.]+)/);
        break;
      case "Firefox":
        match = ua.match(/firefox\/([\d.]+)/);
        break;
      case "Edge":
        match = ua.match(/edg\/([\d.]+)/);
        break;
      default:
        return "Unknown";
    }

    return match ? match[1] : "Unknown";
  }

  // Detect operating system
  static getOS() {
    const ua = this.userAgent;
    const platform = navigator.platform.toLowerCase();

    if (platform.includes("win")) return "Windows";
    if (platform.includes("mac")) return "macOS";
    if (platform.includes("linux")) return "Linux";
    if (ua.includes("android")) return "Android";
    if (ua.includes("iphone") || ua.includes("ipad")) return "iOS";

    return "Unknown";
  }

  // Detect device type
  static getDeviceType() {
    const ua = this.userAgent;

    if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {
      return "tablet";
    }

    if (
      /Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(
        ua
      )
    ) {
      return "mobile";
    }

    return "desktop";
  }

  // Get complete information
  static getInfo() {
    return {
      browser: this.getBrowserName(),
      version: this.getBrowserVersion(),
      os: this.getOS(),
      deviceType: this.getDeviceType(),
      platform: navigator.platform,
      userAgent: navigator.userAgent,
    };
  }

  // Display browser information
  static displayInfo() {
    const info = this.getInfo();

    console.log("=== Browser Information ===");
    console.log(`Browser: ${info.browser} ${info.version}`);
    console.log(`Operating System: ${info.os}`);
    console.log(`Device Type: ${info.deviceType}`);
    console.log(`Platform Info: ${info.platform}`);
  }
}

// Usage
BrowserDetector.displayInfo();
// Output:
// === Browser Information ===
// Browser: Chrome 120.0.0.0
// Operating System: Windows
// Device Type: desktop
// Platform Info: Win32

Language and Internationalization

javascript
// Browser preferred language
console.log(navigator.language);
// e.g.: 'zh-CN', 'en-US', 'ja-JP'

// All supported languages
console.log(navigator.languages);
// e.g.: ['zh-CN', 'zh', 'en']

// Practical application: Automatic language detection
class LanguageDetector {
  static getSupportedLanguages() {
    return ["en", "zh", "ja", "es", "fr", "de"];
  }

  static detectLanguage() {
    const userLang = navigator.language || navigator.languages[0];
    const langCode = userLang.split("-")[0]; // 'zh-CN' -> 'zh'

    const supported = this.getSupportedLanguages();

    if (supported.includes(langCode)) {
      return langCode;
    }

    return "en"; // Default language
  }

  static setPageLanguage() {
    const lang = this.detectLanguage();

    // Set HTML language attribute
    document.documentElement.lang = lang;

    // Load corresponding language pack
    console.log(`Detected language: ${lang}`);

    // In real application, load corresponding language file
    // this.loadLanguagePack(lang);

    return lang;
  }

  static async loadLanguagePack(lang) {
    try {
      const response = await fetch(`/i18n/${lang}.json`);
      const translations = await response.json();

      console.log(`Loaded language pack: ${lang}`, translations);
      return translations;
    } catch (error) {
      console.error(`Failed to load language pack: ${lang}`, error);
      return {};
    }
  }
}

// Usage
const language = LanguageDetector.setPageLanguage();
console.log("Page language:", language);

Online Status Detection

javascript
// Detect browser's current online status
console.log("Online status:", navigator.onLine); // true or false

// Listen for online/offline events
window.addEventListener("online", () => {
  console.log("Network connected");
  document.body.classList.remove("offline");
  document.body.classList.add("online");
});

window.addEventListener("offline", () => {
  console.log("Network disconnected");
  document.body.classList.remove("online");
  document.body.classList.add("offline");
});

// Practical application: Network status manager
class NetworkManager {
  constructor() {
    this.listeners = [];
    this.init();
  }

  init() {
    window.addEventListener("online", () => this.handleOnline());
    window.addEventListener("offline", () => this.handleOffline());

    // Initial status
    if (!navigator.onLine) {
      this.handleOffline();
    }
  }

  handleOnline() {
    console.log("Network restored");
    this.showNotification("Network connected", "success");
    this.syncOfflineData();
    this.notifyListeners("online");
  }

  handleOffline() {
    console.log("Network disconnected");
    this.showNotification("Network disconnected, some features may be unavailable", "warning");
    this.enableOfflineMode();
    this.notifyListeners("offline");
  }

  showNotification(message, type = "info") {
    console.log(`[${type.toUpperCase()}] ${message}`);

    // In real application, display friendly notification UI
    /*
    const notification = document.createElement('div');
    notification.className = `notification ${type}`;
    notification.textContent = message;
    document.body.appendChild(notification);

    setTimeout(() => notification.remove(), 3000);
    */
  }

  enableOfflineMode() {
    console.log("Enabling offline mode");

    // Disable features that require network
    const onlineOnlyButtons = document.querySelectorAll("[data-online-only]");
    onlineOnlyButtons.forEach((btn) => {
      btn.disabled = true;
      btn.title = "This feature requires network connection";
    });
  }

  async syncOfflineData() {
    console.log("Syncing offline data...");

    // In real application, get offline data from IndexedDB or localStorage and sync
    const offlineData = this.getOfflineData();

    if (offlineData.length > 0) {
      try {
        // Sync data to server
        // await fetch('/api/sync', {
        //   method: 'POST',
        //   body: JSON.stringify(offlineData)
        // });

        console.log("Offline data sync complete");
        this.clearOfflineData();
      } catch (error) {
        console.error("Sync failed:", error);
      }
    }
  }

  getOfflineData() {
    // Get offline data from local storage
    return []; // Simplified example
  }

  clearOfflineData() {
    // Clear synced offline data
  }

  onStatusChange(callback) {
    this.listeners.push(callback);
  }

  notifyListeners(status) {
    this.listeners.forEach((callback) => callback(status));
  }

  isOnline() {
    return navigator.onLine;
  }
}

// Usage
const networkManager = new NetworkManager();

networkManager.onStatusChange((status) => {
  console.log("Network status changed:", status);
});
javascript
// Detect if Cookies are enabled
console.log("Cookies enabled:", navigator.cookieEnabled);

if (!navigator.cookieEnabled) {
  alert("Please enable Cookies for the best experience");
}

// Detect Do Not Track setting
console.log("Do Not Track:", navigator.doNotTrack);
// '1' = enabled, '0' = not enabled, null = not set

Geolocation API

javascript
// Detect if Geolocation API is available
if ("geolocation" in navigator) {
  console.log("Geolocation API supported");

  // Get current position
  navigator.geolocation.getCurrentPosition(
    (position) => {
      console.log("Location information:");
      console.log("  Latitude:", position.coords.latitude);
      console.log("  Longitude:", position.coords.longitude);
      console.log("  Accuracy:", position.coords.accuracy, "meters");
      console.log("  Altitude:", position.coords.altitude);
      console.log("  Timestamp:", new Date(position.timestamp));
    },
    (error) => {
      console.error("Failed to get location:", error.message);

      switch (error.code) {
        case error.PERMISSION_DENIED:
          console.log("User denied geolocation request");
          break;
        case error.POSITION_UNAVAILABLE:
          console.log("Location information unavailable");
          break;
        case error.TIMEOUT:
          console.log("Request timed out");
          break;
      }
    },
    {
      enableHighAccuracy: true, // High accuracy mode
      timeout: 5000, // Timeout (milliseconds)
      maximumAge: 0, // Cache time (0 = don't use cache)
    }
  );
} else {
  console.log("Geolocation API not supported");
}

// Practical application: Geolocation manager
class GeolocationManager {
  static async getCurrentPosition() {
    return new Promise((resolve, reject) => {
      if (!("geolocation" in navigator)) {
        reject(new Error("Browser does not support Geolocation API"));
        return;
      }

      navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            accuracy: position.coords.accuracy,
            timestamp: position.timestamp,
          });
        },
        (error) => {
          reject(error);
        },
        {
          enableHighAccuracy: true,
          timeout: 10000,
          maximumAge: 60000, // Cache 1 minute
        }
      );
    });
  }

  static watchPosition(callback) {
    if (!("geolocation" in navigator)) {
      console.error("Browser does not support Geolocation API");
      return null;
    }

    const watchId = navigator.geolocation.watchPosition(
      (position) => {
        callback({
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
          accuracy: position.coords.accuracy,
          timestamp: position.timestamp,
        });
      },
      (error) => {
        console.error("Location watch error:", error);
      },
      {
        enableHighAccuracy: true,
        timeout: 5000,
        maximumAge: 0,
      }
    );

    return watchId;
  }

  static stopWatching(watchId) {
    if (watchId !== null) {
      navigator.geolocation.clearWatch(watchId);
    }
  }

  static async getAddressFromCoords(lat, lng) {
    // In real application, call geocoding API
    console.log(`Getting address for: ${lat}, ${lng}`);

    // Example: Use OpenStreetMap Nominatim API
    try {
      const response = await fetch(
        `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`
      );
      const data = await response.json();
      return data.display_name;
    } catch (error) {
      console.error("Failed to get address:", error);
      return null;
    }
  }
}

// Usage
async function showCurrentLocation() {
  try {
    const position = await GeolocationManager.getCurrentPosition();
    console.log("Current location:", position);

    const address = await GeolocationManager.getAddressFromCoords(
      position.latitude,
      position.longitude
    );
    console.log("Address:", address);
  } catch (error) {
    console.error("Failed to get location:", error.message);
  }
}

// Watch position changes
// const watchId = GeolocationManager.watchPosition((position) => {
//   console.log('Position updated:', position);
// });

Screen Object Detailed

The screen object provides various information about the user's screen, primarily used for screen adaptation and display optimization.

Screen Dimension Properties

javascript
// Total screen dimensions (including taskbar, etc.)
console.log("Screen width:", screen.width); // e.g.: 1920
console.log("Screen height:", screen.height); // e.g.: 1080

// Available screen dimensions (excluding taskbar, etc.)
console.log("Available width:", screen.availWidth); // e.g.: 1920
console.log("Available height:", screen.availHeight); // e.g.: 1040

// Calculate space occupied by taskbar
const taskbarHeight = screen.height - screen.availHeight;
console.log("Taskbar height:", taskbarHeight); // e.g.: 40

// Practical application: Detect screen information
function getScreenInfo() {
  return {
    total: {
      width: screen.width,
      height: screen.height,
    },
    available: {
      width: screen.availWidth,
      height: screen.availHeight,
    },
    taskbar: {
      height: screen.height - screen.availHeight,
      width: screen.width - screen.availWidth,
    },
    aspectRatio: (screen.width / screen.height).toFixed(2),
  };
}

console.log("Screen information:", getScreenInfo());

Color Depth and Pixel Depth

javascript
// Color depth (bits)
console.log("Color depth:", screen.colorDepth);
// Usually 24 (approximately 16.7 million colors)

// Pixel depth (usually same as color depth)
console.log("Pixel depth:", screen.pixelDepth);

// Practical application: Color support detection
class ColorSupportDetector {
  static getColorSupport() {
    const depth = screen.colorDepth;

    if (depth >= 24) {
      return "truecolor"; // True color (16.7M+ colors)
    } else if (depth >= 16) {
      return "highcolor"; // High color (65,536 colors)
    } else if (depth >= 8) {
      return "lowcolor"; // 256 colors
    } else {
      return "monochrome"; // Monochrome
    }
  }

  static canDisplay(colorMode) {
    const support = this.getColorSupport();
    const levels = ["monochrome", "lowcolor", "highcolor", "truecolor"];

    return levels.indexOf(support) >= levels.indexOf(colorMode);
  }

  static adjustImageQuality() {
    const support = this.getColorSupport();

    if (support === "truecolor") {
      console.log("Use high quality images");
      return "high";
    } else if (support === "highcolor") {
      console.log("Use medium quality images");
      return "medium";
    } else {
      console.log("Use low quality images");
      return "low";
    }
  }
}

// Usage
console.log("Color support:", ColorSupportDetector.getColorSupport());
console.log("Can display true color:", ColorSupportDetector.canDisplay("truecolor"));

const imageQuality = ColorSupportDetector.adjustImageQuality();

Screen Orientation

javascript
// Screen orientation (modern browsers)
if (screen.orientation) {
  console.log("Screen orientation:", screen.orientation.type);
  // 'portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary'

  console.log("Rotation angle:", screen.orientation.angle);
  // 0, 90, 180, 270

  // Listen for orientation changes
  screen.orientation.addEventListener("change", () => {
    console.log("Orientation changed:", screen.orientation.type);
    console.log("Rotation angle:", screen.orientation.angle);
  });
}

// Practical application: Screen orientation manager
class OrientationManager {
  constructor() {
    this.listeners = [];
    this.init();
  }

  init() {
    if (!screen.orientation) {
      console.warn("Browser does not support screen.orientation API");
      return;
    }

    screen.orientation.addEventListener("change", () => {
      this.handleOrientationChange();
    });

    // Initial detection
    this.handleOrientationChange();
  }

  handleOrientationChange() {
    const orientation = this.getCurrentOrientation();
    console.log("Current orientation:", orientation);

    // Update body class
    document.body.classList.remove("portrait", "landscape");
    document.body.classList.add(orientation);

    // Notify listeners
    this.notifyListeners(orientation);
  }

  getCurrentOrientation() {
    if (screen.orientation) {
      return screen.orientation.type.includes("portrait")
        ? "portrait"
        : "landscape";
    }

    // Fallback: Use window dimensions to determine
    return window.innerHeight > window.innerWidth ? "portrait" : "landscape";
  }

  isPortrait() {
    return this.getCurrentOrientation() === "portrait";
  }

  isLandscape() {
    return this.getCurrentOrientation() === "landscape";
  }

  onChange(callback) {
    this.listeners.push(callback);
  }

  notifyListeners(orientation) {
    this.listeners.forEach((callback) => callback(orientation));
  }

  async lockOrientation(orientation) {
    if (!screen.orientation || !screen.orientation.lock) {
      console.warn("Browser does not support locking screen orientation");
      return false;
    }

    try {
      await screen.orientation.lock(orientation);
      console.log("Screen orientation locked:", orientation);
      return true;
    } catch (error) {
      console.error("Failed to lock orientation:", error);
      return false;
    }
  }

  unlockOrientation() {
    if (screen.orientation && screen.orientation.unlock) {
      screen.orientation.unlock();
      console.log("Screen orientation unlocked");
    }
  }
}

// Usage
const orientationManager = new OrientationManager();

orientationManager.onChange((orientation) => {
  console.log("Orientation changed:", orientation);

  if (orientation === "portrait") {
    console.log("Apply portrait layout");
  } else {
    console.log("Apply landscape layout");
  }
});

Comprehensive Practical Application Example

Device Adaptation Manager

javascript
class DeviceAdapter {
  static getDeviceInfo() {
    const browser = BrowserDetector.getInfo();
    const screen = getScreenInfo();

    return {
      browser,
      screen,
      viewport: {
        width: window.innerWidth,
        height: window.innerHeight,
      },
      devicePixelRatio: window.devicePixelRatio || 1,
      touchSupport: "ontouchstart" in window,
      online: navigator.onLine,
      language: navigator.language,
    };
  }

  static getOptimalImageSize() {
    const dpr = window.devicePixelRatio || 1;
    const width = window.innerWidth;

    // Select appropriate image size based on DPR and screen width
    if (dpr >= 3) {
      return width < 768 ? "2x" : "3x";
    } else if (dpr >= 2) {
      return "2x";
    } else {
      return "1x";
    }
  }

  static shouldLoadHighQualityAssets() {
    // Consider multiple factors
    const hasGoodConnection = navigator.connection
      ? navigator.connection.effectiveType === "4g"
      : true;

    const hasGoodScreen = screen.width >= 1920 && screen.colorDepth >= 24;

    const hasGoodBrowser = !BrowserDetector.getBrowserName().includes("IE");

    return hasGoodConnection && hasGoodScreen && hasGoodBrowser;
  }

  static applyOptimizations() {
    const info = this.getDeviceInfo();

    console.log("=== Device Adaptation ===");
    console.log("Device information:", info);

    // Apply optimizations based on device capabilities
    if (info.devicePixelRatio > 1) {
      console.log("High DPI screen, load 2x/3x images");
      document.body.classList.add("retina");
    }

    if (!info.touchSupport) {
      console.log("Non-touch device, enable hover effects");
      document.body.classList.add("no-touch");
    } else {
      console.log("Touch device, disable hover effects");
      document.body.classList.add("touch");
    }

    if (info.screen.total.width < 768) {
      console.log("Small screen device, apply mobile optimizations");
      document.body.classList.add("mobile-optimized");
    }

    if (this.shouldLoadHighQualityAssets()) {
      console.log("Device has good capabilities, load high quality resources");
      document.body.classList.add("high-quality");
    } else {
      console.log("Device has limited capabilities, load standard quality resources");
      document.body.classList.add("standard-quality");
    }
  }
}

// Usage
DeviceAdapter.applyOptimizations();

Feature Detection Utility Set

javascript
class FeatureDetector {
  // Detect various API support
  static checkSupport() {
    return {
      geolocation: "geolocation" in navigator,
      localStorage: this.testLocalStorage(),
      sessionStorage: this.testSessionStorage(),
      indexedDB: "indexedDB" in window,
      serviceWorker: "serviceWorker" in navigator,
      webWorker: typeof Worker !== "undefined",
      webSocket: "WebSocket" in window,
      notification: "Notification" in window,
      vibration: "vibrate" in navigator,
      batteryAPI: "getBattery" in navigator,
      networkInfo: "connection" in navigator,
      mediaDevices: "mediaDevices" in navigator,
      clipboard: "clipboard" in navigator,
      share: "share" in navigator,
      screenOrientation: "orientation" in screen,
    };
  }

  static testLocalStorage() {
    try {
      const test = "__test__";
      localStorage.setItem(test, test);
      localStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  }

  static testSessionStorage() {
    try {
      const test = "__test__";
      sessionStorage.setItem(test, test);
      sessionStorage.removeItem(test);
      return true;
    } catch (e) {
      return false;
    }
  }

  static displaySupport() {
    const support = this.checkSupport();

    console.log("=== Browser Feature Support ===");

    Object.entries(support).forEach(([feature, supported]) => {
      const status = supported ? "✓" : "✗";
      console.log(`${status} ${feature}`);
    });
  }

  static getMissingFeatures(required) {
    const support = this.checkSupport();
    return required.filter((feature) => !support[feature]);
  }

  static checkRequirements(required) {
    const missing = this.getMissingFeatures(required);

    if (missing.length > 0) {
      console.warn("Missing required features:", missing);
      return false;
    }

    console.log("All required features are supported");
    return true;
  }
}

// Usage
FeatureDetector.displaySupport();

// Check specific feature requirements
const requiredFeatures = ["geolocation", "localStorage", "serviceWorker"];
if (!FeatureDetector.checkRequirements(requiredFeatures)) {
  alert("Your browser doesn't support some required features. Please upgrade your browser.");
}

Best Practices

1. Use Feature Detection Rather Than Browser Detection

javascript
// ❌ Not recommended: Browser detection
if (navigator.userAgent.includes("Chrome")) {
  // Use some feature
}

// ✅ Recommended: Feature detection
if ("geolocation" in navigator) {
  // Use geolocation feature
}

if (typeof Worker !== "undefined") {
  // Use Web Worker
}

2. Graceful Degradation

javascript
class StorageManager {
  static set(key, value) {
    if (FeatureDetector.testLocalStorage()) {
      localStorage.setItem(key, value);
    } else {
      // Fallback: Use Cookie
      document.cookie = `${key}=${value}; path=/`;
    }
  }

  static get(key) {
    if (FeatureDetector.testLocalStorage()) {
      return localStorage.getItem(key);
    } else {
      // Read from Cookie
      const match = document.cookie.match(new RegExp(`${key}=([^;]+)`));
      return match ? match[1] : null;
    }
  }
}

3. Privacy Protection

javascript
// Respect user's Do Not Track setting
if (navigator.doNotTrack === "1") {
  console.log("User enabled Do Not Track, don't collect statistics");
  // Disable tracking features
} else {
  console.log("Can collect anonymous statistics");
  // Enable tracking features (requires user consent)
}

Summary

navigator and screen objects are important tools for getting browser and screen information:

Navigator Object:

  • Browser information (userAgent, appName, appVersion)
  • System information (platform, language)
  • Online status (onLine)
  • Geolocation (geolocation)
  • Cookie support (cookieEnabled)

Screen Object:

  • Screen dimensions (width, height, availWidth, availHeight)
  • Color depth (colorDepth, pixelDepth)
  • Screen orientation (orientation)

Best Practices:

  1. Use feature detection rather than browser detection
  2. Implement graceful degradation
  3. Respect user privacy settings
  4. Consider multiple factors for device adaptation
  5. Cache detection results to improve performance