在授予的地理定位权限上附加事件侦听器

Attach event listener on geolocation permission granted

我的设置:

我有一些非常简单的代码:

const onSuccess = () => {
    console.log('success');
}

const onError = () => {
    console.log('error');
}

navigator.geolocation.getCurrentPosition(onSuccess, onError);

我的实际代码包含更多逻辑。但为简单起见,我只发布了所需的代码。

目前一切顺利...

实际问题:

现在,如果用户阻止地理定位并且再次允许浏览器使用地理定位,那么在那个时候,success 应该被记录。但是在每次刷新时,它不应该记录 success.

所以,我相信,允许按钮上应该有某种事件侦听器,但我没有看到任何地方提到它。

我的研究:

我不确定我的方向是否正确,但是...

在 MDN 文档 here 上,我可以看到有一个叫做 PermissionStatus.onchange.

的东西

但它是实验性的,不支持 Internet Explorer、Safari 和 Safari iOS。

如果你们中有人使用过任何替代方案,那么我很乐意检查你的想法。

实际用例

我正在开发天气应用程序。要求是:

我的实际技术栈

更新:


我收到了一些将用户权限状态存储到本地存储的建议。

但是,如果我将权限存储在本地存储中,则会遇到一些问题。

我举个例子详细解释一下

部分直到一切正常:

当我遇到问题时:

您可以将权限状态保存到本地存储,并在每次页面刷新时检查权限是否已更改:

const onSuccess = () => {
console.log('success');
    if (localStorage.getItem("permission_status") !== "ok")  {
        // fire permission onchange event
        // update local storage
    }
}

const onError = () => {
    console.log('error');
    if (localStorage.getItem("permission_status") === "ok")  {
        // fire permission onchange event
        // update local storage
    }
}


navigator.geolocation.getCurrentPosition(onSuccess, onError);

我的建议不是保存用户的权限状态,而是在成功回调中保存用户的 GeolocationPosition 对象。 所以你可以比较对象而不是比较权限状态。 由于 macOS 浏览器不支持,您无法使用 navigator.permissions,唯一的解决方案是保存坐标数据本身以供比较。

因此您的代码将如下所示:

const onSuccess = (pos) => {
  if (localStorage.getItem("latitude") !== pos.coords.latitude || 
      localStorage.getItem("longitude") !== pos.coords.longitude)
  {
    // fire permission onchange event
    // update local storage
    console.log('success');
  }
}

const onError = () => {
    console.log('error');
}

navigator.geolocation.getCurrentPosition(onSuccess, onError);

编辑 刚看到你对@Zeke Hernandez 的评论。这是独立于权限的解决方案,所以我认为它应该适合你。 我不知道你是如何渲染用户的城市详情页面,但我相信你会使用用户的位置数据来渲染页面,我们可以在成功回调中使用我们保存到本地存储的数据。 我没有看到其他可能性或 alternative.s

等了一段时间,除了使用local-storage,我没有得到任何解决方案。所以,我采取了混合方法。在我的方法中,我在实现它的浏览器上使用 navigator.permissions,对于其他浏览器,我使用 localstorage.

我正在使用 React,所以我的解决方案包括 React 代码,但这个想法仍然可以用于纯 javascript 或任何 javascript 框架。

我为此创建了一个自定义挂钩:

useGeoLocation.ts

import { useState, useEffect } from 'react';

interface Coordinates {
  latitude: number;
  longitude: number;
};

const useGeoLocation = (localStorageKey = 'is-location-granted') => {
  const [shouldRequestLocation, setShouldRequestLocation] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<GeolocationPositionError>();
  const [coords, setCoords] = useState<Coordinates>();

  const onSuccess = (location: GeolocationPosition) => {
    setLoading(false);
    setCoords({
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
    });
    if (!('permissions' in navigator)) {
      localStorage.setItem(localStorageKey, JSON.stringify(true));
    }
  }

  const onError = (error: GeolocationPositionError) => {
    setLoading(false);
    setError(error);
  }

  useEffect(() => {
    if ('permissions' in navigator) {
      navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => {
        if (permissionStatus.state !== 'granted') {
          setShouldRequestLocation(true);
        }
      });
    } else {
      const isLocationGranted = JSON.parse(localStorage.getItem(localStorageKey) || 'false');
      if (!isLocationGranted) {
        setShouldRequestLocation(true);
      }
    }
  }, []);

  useEffect(() => {
    if(!('geolocation' in navigator)) {
      const geoError = new GeolocationPositionError();
      setError({
        ...geoError,
        message: 'Geolocation not supported'
      });
    } else if (shouldRequestLocation) {
      setLoading(true);
      navigator.geolocation.getCurrentPosition(
        onSuccess,
        onError,
        { enableHighAccuracy: false, timeout: 10000, maximumAge: 18000000 },
      );
    }
  }, [shouldRequestLocation]);

  return { loading, error, coords };
};

export default useGeoLocation;

代码非常简单,但如果有人发现任何困难,请告诉我,我会相应地更新我的答案。