Skip to content

Geolocation API 基础

Geolocation API 允许网页在用户授权后获取设备的地理位置信息。它是 HTML5 的重要特性之一,广泛应用于地图定位、附近搜索、天气服务等场景。本节将介绍 Geolocation API 的基本用法、权限机制和错误处理。

前置知识

阅读本节前,建议先了解:Cookie 对比

基础概念

Geolocation API 概述

Geolocation API 通过 navigator.geolocation 对象提供位置服务。它可以获取设备的纬度、经度、海拔等地理信息,并支持一次性获取和持续追踪两种模式。

特性说明
API 对象navigator.geolocation
安全要求必须在安全上下文(HTTPS)中使用
用户授权首次使用时需要用户同意
数据来源GPS、WiFi、IP 地址、蓝牙等

浏览器支持

浏览器支持版本
Chrome支持5.0+
Firefox支持3.5+
Safari支持5.0+
Edge支持12+
IE支持9.0+
移动端支持Android 2.0+, iOS 3.0+

语法与 API

检测 API 可用性

javascript
// 检测 Geolocation API 是否可用
if ('geolocation' in navigator) {
  console.log('Geolocation API 可用');
} else {
  console.log('Geolocation API 不可用');
  // 提示用户升级浏览器
}

// 更详细的检测
function checkGeolocationSupport() {
  const result = {
    supported: 'geolocation' in navigator,
    secureContext: window.isSecureContext,
    permission: null
  };

  // 检测权限状态
  if (navigator.permissions) {
    navigator.permissions.query({ name: 'geolocation' }).then(status => {
      result.permission = status.state; // 'granted' | 'denied' | 'prompt'
      console.log('Geolocation 状态:', result);
    });
  }

  return result;
}

获取当前位置 - getCurrentPosition

javascript
// 基本用法
navigator.geolocation.getCurrentPosition(
  onSuccess,  // 成功回调
  onError,     // 失败回调
  options     // 可选配置
);

// 成功回调
function onSuccess(position) {
  console.log('位置获取成功:', position);
}

// 失败回调
function onError(error) {
  console.error('位置获取失败:', error);
}

// 配置选项
const options = {
  enableHighAccuracy: true,  // 高精度模式
  timeout: 10000,            // 超时时间(毫秒)
  maximumAge: 0              // 缓存有效期(毫秒)
};

详细说明

权限机制

Geolocation API 必须获得用户授权才能获取位置。浏览器会弹出权限请求对话框:

javascript
// 权限状态查询
if (navigator.permissions) {
  navigator.permissions.query({ name: 'geolocation' }).then(result => {
    switch (result.state) {
      case 'granted':
        console.log('已授权,可以直接获取位置');
        getLocation();
        break;
      case 'denied':
        console.log('用户已拒绝,无法获取位置');
        showDeniedMessage();
        break;
      case 'prompt':
        console.log('未决定,将弹出授权请求');
        break;
    }

    // 监听权限变化
    result.addEventListener('change', () => {
      console.log('权限状态变为:', result.state);
    });
  });
}

错误处理

javascript
function onError(error) {
  switch (error.code) {
    case error.PERMISSION_DENIED:
      // 用户拒绝了位置请求
      console.error('用户拒绝了位置授权');
      showMessage('请允许获取位置权限以使用此功能');
      break;

    case error.POSITION_UNAVAILABLE:
      // 无法获取位置信息
      console.error('位置信息不可用');
      showMessage('无法获取您的位置,请检查定位服务');
      break;

    case error.TIMEOUT:
      // 请求超时
      console.error('获取位置超时');
      showMessage('获取位置超时,请重试');
      break;

    default:
      console.error('未知错误:', error.message);
      showMessage('获取位置时发生未知错误');
  }
}

// 错误对象属性
// error.code    - 错误码(1=拒绝, 2=不可用, 3=超时)
// error.message - 错误描述

配置选项详解

javascript
const options = {
  // 高精度模式:使用 GPS(更精确但更耗电)
  enableHighAccuracy: false,

  // 超时时间:获取位置的最大等待时间
  timeout: 10000, // 10 秒

  // 缓存有效期:接受缓存位置的最大年龄
  // 0 = 每次都获取新位置
  // Infinity = 永远使用缓存
  maximumAge: 300000 // 5 分钟
};
选项类型默认值说明
enableHighAccuracybooleanfalse是否使用高精度(GPS)
timeoutlongInfinity超时时间(毫秒)
maximumAgelong0缓存位置的最大年龄

实战示例

完整位置获取示例

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Geolocation API 基础演示</title>
  <style>
    body { font-family: -apple-system, sans-serif; padding: 20px; background: #f5f7fa; }

    .geo-card {
      max-width: 500px;
      margin: 0 auto;
      padding: 24px;
      background: white;
      border-radius: 12px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }

    h2 { text-align: center; color: #1a1a2e; }

    .location-btn {
      display: block;
      width: 100%;
      padding: 14px;
      background: #1a73e8;
      color: white;
      border: none;
      border-radius: 8px;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      transition: background 0.2s;
    }

    .location-btn:hover { background: #1557b0; }
    .location-btn:disabled {
      background: #ccc;
      cursor: not-allowed;
    }

    .result {
      margin-top: 20px;
      padding: 16px;
      background: #f8f9fa;
      border-radius: 8px;
      display: none;
    }

    .result.show { display: block; }

    .result-row {
      display: flex;
      justify-content: space-between;
      padding: 8px 0;
      border-bottom: 1px solid #eee;
    }

    .result-row:last-child { border-bottom: none; }
    .result-label { color: #666; font-weight: 600; }
    .result-value { color: #333; font-family: monospace; }

    .message {
      margin-top: 12px;
      padding: 12px;
      border-radius: 8px;
      font-size: 14px;
      display: none;
    }

    .message.show { display: block; }
    .message.error { background: #fee2e2; color: #dc2626; }
    .message.success { background: #d1fae5; color: #065f46; }
    .message.info { background: #dbeafe; color: #2563eb; }

    .status {
      text-align: center;
      padding: 8px;
      color: #888;
      font-size: 13px;
    }
  </style>
</head>
<body>
  <div class="geo-card">
    <h2>获取当前位置</h2>

    <button class="location-btn" id="getLocationBtn" onclick="getLocation()">
      获取我的位置
    </button>

    <div class="status" id="status"></div>

    <div class="message" id="message"></div>
    <div class="result" id="result"></div>
  </div>

  <script>
    const btn = document.getElementById('getLocationBtn');
    const status = document.getElementById('status');
    const messageEl = document.getElementById('message');
    const resultEl = document.getElementById('result');

    function showMessage(text, type = 'info') {
      messageEl.textContent = text;
      messageEl.className = `message show ${type}`;
    }

    function hideMessage() {
      messageEl.className = 'message';
    }

    // 检测 API 支持
    if (!('geolocation' in navigator)) {
      btn.disabled = true;
      showMessage('您的浏览器不支持 Geolocation API', 'error');
    }

    // 检查 HTTPS
    if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
      showMessage('Geolocation API 需要 HTTPS 协议', 'error');
    }

    function getLocation() {
      hideMessage();

      // 检查权限
      if (navigator.permissions) {
        navigator.permissions.query({ name: 'geolocation' }).then(result => {
          if (result.state === 'denied') {
            showMessage('您已拒绝位置权限,请在浏览器设置中开启', 'error');
            return;
          }
          requestPosition();
        });
      } else {
        requestPosition();
      }
    }

    function requestPosition() {
      btn.disabled = true;
      status.textContent = '正在获取位置...';

      const options = {
        enableHighAccuracy: true,
        timeout: 10000,
        maximumAge: 0
      };

      navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
    }

    function onSuccess(position) {
      btn.disabled = false;
      status.textContent = '';

      const coords = position.coords;

      showMessage('位置获取成功', 'success');

      resultEl.className = 'result show';
      resultEl.innerHTML = `
        <div class="result-row">
          <span class="result-label">纬度</span>
          <span class="result-value">${coords.latitude.toFixed(6)}</span>
        </div>
        <div class="result-row">
          <span class="result-label">经度</span>
          <span class="result-value">${coords.longitude.toFixed(6)}</span>
        </div>
        <div class="result-row">
          <span class="result-label">精度</span>
          <span class="result-value">${coords.accuracy.toFixed(0)} 米</span>
        </div>
        <div class="result-row">
          <span class="result-label">海拔</span>
          <span class="result-value">${coords.altitude !== null ? coords.altitude.toFixed(0) + ' 米' : '不可用'}</span>
        </div>
        <div class="result-row">
          <span class="result-label">速度</span>
          <span class="result-value">${coords.speed !== null ? coords.speed.toFixed(1) + ' m/s' : '不可用'}</span>
        </div>
        <div class="result-row">
          <span class="result-label">获取时间</span>
          <span class="result-value">${new Date(position.timestamp).toLocaleString()}</span>
        </div>
      `;

      console.log('完整位置对象:', position);
    }

    function onError(error) {
      btn.disabled = false;
      status.textContent = '';

      switch (error.code) {
        case error.PERMISSION_DENIED:
          showMessage('用户拒绝了位置授权请求', 'error');
          break;
        case error.POSITION_UNAVAILABLE:
          showMessage('无法获取位置信息,请检查设备定位服务', 'error');
          break;
        case error.TIMEOUT:
          showMessage('获取位置超时,请检查网络或重试', 'error');
          break;
        default:
          showMessage(`位置获取失败: ${error.message}`, 'error');
      }

      console.error('Geolocation 错误:', error);
    }
  </script>
</body>
</html>

注意事项

HTTPS 要求

现代浏览器要求 Geolocation API 必须在安全上下文中使用:

环境是否支持
https://支持
http://localhost支持
http://127.0.0.1支持
file://部分浏览器支持
http://(其他域名)不支持

精度与耗电

模式精度耗电量数据来源
高精度 (GPS)~10 米GPS 卫星
低精度 (WiFi/IP)~100-1000 米WiFi 热点、IP 地址
城市级 (IP)~公里级极低IP 地理数据库

最佳实践

  1. 先检查权限状态,避免不必要的请求
  2. 设置合理的超时时间,提升用户体验
  3. 处理所有错误状态,提供友好的降级提示
  4. 不要频繁请求高精度定位,避免耗电
  5. 在 HTTPS 下使用,否则浏览器会拒绝

下一节

继续学习:获取位置信息

参考链接