简体   繁体   中英

function fail never called if user declines to share geolocation in firefox

So I have this javascript code. In safari and chrome, if user declines to share location, it goes to fail function as it should; however, in Firefox, it does not.

Any helps appreciated.

function initGeolocation()
{
    if( navigator.geolocation )
    {          
          // Call getCurrentPosition with success and failure callbacks
          navigator.geolocation.getCurrentPosition( success, fail );
    }
    else
    {
          alert("Sorry, your browser does not support geolocation services.");
    }
}

 var map;
 function success(position)
 {

     var longIDText = document.getElementById('longID');
     var latIDText = document.getElementById('latID');
     longIDText.value = position.coords.longitude;
     latIDText.value = position.coords.latitude;
     document.getElementById('coordSubmitID').click();
  }

  function fail(error)
  {
          alert("FAAAAAAAAAAIIIIIIIIIL")
          var zip_code ;
          while (true){
              // Could not obtain location

              zip_code = prompt("Please enter your current address or zip code","");


              if ( zip_code == "" ) {
                  alert(zip_code +" is not a valid address. Please try again.");
              } 
              else{
                break;
              }
          }
          var zipIDText = document.getElementById('zipID');
          zipIDText.value = zip_code;
          document.getElementById('coordSubmitID').click();
  }

For Firefox it seems that PERMISSION_DENIED is raised only if "Never share" is selected; if the dialog is dismissed or "Not now" is selected, effectively nothing happens - even on mozillas geolocation demo if you dismiss the permissions UI nothing happens.

This means that getCurrentPosition can return either because the user closed the confirmation UI, or because it successfully started it asynchronous request - there doesn't appear to be a way to discriminate between the two.

https://bugzilla.mozilla.org/show_bug.cgi?id=675533

This is a real pain, and definately not desirable functionality.

The workaround I am using to save the user waiting for ever is to set a timeout to check if the wait spinner is showing after 3 seconds, and if so, hide it and show a manual zip code input:

var latlng;

var waitTime = 3000;
try {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function (position) {
            success(position);
        }, showError);
    } else {
        showError("NOT-SUPPORTED");
    }
    var t = setTimeout(function () {
        if ($("#getZip div.loading").css("display") != "none") {
            $("#getZip div.loading").hide();
            $("#errorZip").show();
        }
    }, waitTime);
} catch (evt) {
    alert(evt);
}

As alexleonard pointed out, the only reliable, cross-browser solution I've found for this is to have a setTimeout() to check the state of your latLng object/variables, as the timeout option of getCurrentPosition() doesn't seem to work reliably. The example below was tested in IE11, Chrome33 and Firefox28.

For a more complete solution that uses a jQuery promise, check out my Gist here: https://gist.github.com/GFoley83/10092929

Example - Hit F12, paste into the console and run

var latLng,
    geoOptions = {
        enableHighAccuracy: false,
        timeout: 5000, // Wait 5 seconds
        maximumAge: 300000 //  Valid for 5 minutes
    };

var userLocationFound = function(position){
    latLng = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
    };
    window.console.log("User confirmed! Location found: " + latLng.lat + ", " + latLng .lng);
}

var userLocationNotFound = function(){
    latLng = {
        lat: -41.29247, // fallback lat 
        lng: 174.7732  // fallback lng
    };
    window.console.log("Fallback set: ", latLng);
}

window.navigator.geolocation.getCurrentPosition(userLocationFound, userLocationNotFound, geoOptions);

setTimeout(function () {
    if(!latLng){
        window.console.log("No confirmation from user, using fallback");
        userLocationNotFound();
    }else{
        window.console.log("Location was set");
    }
}, geoOptions.timeout + 1000); // Wait extra second

This is as per design and apparently is a non-issue. See https://bugzilla.mozilla.org/show_bug.cgi?id=675533

It is left to the developers using the feature to come up with a browser-specific work around for Firefox.

Of note, based on the comment by the FF developer about actual geolocation usage by end users, apparently the Firefox developers are gathering detailed data about what end users are doing with their products.

Note that this is actually also an issue in Chrome.

In Chrome an advisory notice appears "site wants to know your location" - it then offers "Allow" and "Deny" as options. With the UI design, the user is more likely to choose either Allow or Deny, which will then return an answer to the navigator.geolocation.getCurrentPosition function. However, the user can also click an "X" on the far right of the advisory notice. This is essentially the same as clicking "Not now" in Firefox.

No result is returned to the function.

Looks like a timer has to be implemented to allow for this possible outcome.

Use a promise. I'm using angularjs that has it's own version of $q to solve the same issue we are all having.

            $scope.ByUserPosition = function () {
                //http://www.w3.org/TR/geolocation-API/
                var deferred = $q.defer();

                deferred.promise.then(findByPosition, function (data) {
                    console.log('error', data);
                });

                var resolveBy = 1000 * 30;
                navigator.geolocation.getCurrentPosition(function (position) {
                    deferred.resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude });
                }, function (err) {
                    deferred.reject(err);
                }, {
                    enableHighAccuracy: true,
                    timeout: resolveBy,
                    maximumAge: 0
                });

                $timeout(function() {
                    deferred.reject('timed out');
                }, resolveBy)
            };

Edit (after a downvote) 6-11-2013

As remarked below this answer, this does not solve the problem when a user denies access to location. To the downvote is correct. I keep the answer here, because the timeout is soemthing one should use anyway, and I do not see it in any answer.

The function navigator.geolocation.getCurrentPosition() has the option to send the timeout with it:

navigator.geolocation.getCurrentPosition(
     function (position){
        //do someting with position
     },
     function (error){
        // do something with the error (like the code)
     }, 
     {timeout:10000}
);

and some other options, like the age of the cached position (maximumAge). the timeout and maximumage are in milliseconds, so 10000 = 10 secs.

By the way, the default timeout is infinite... so if you do not set the timeout, it will never call the callback for errors....

so in the option like Jamie answered, I would say:

if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function (position) {
            success(position);
        }, showError, {timeout:3000});
    }

The only solution to this problem is as follows:

  1. When the user clicks the button to get his location automatically, show the spinner and a link beside it "ex: Cancel".
  2. If he enables sharing location, then no problem. If he decides to dismiss Firefox popup, the spinner would still be shown but he can click "Cancel" link to stop the spinner an an indicator for dismissing the popup

I hope this helps :)

Here's a solution to my problem using vanilla es6 Promises . Inspired by @LeblancMeneses response.

if(navigator.geolocation) {
  let promise = new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        resolve(position)
      },
      (error) => {
        resolve(error)
      }
    )
    window.setTimeout(() => {
      resolve({})
    }, 8000)
  })
  promise.then((result) => {
      // do something with result
  })
} else {
  console.log('no geolocation available')
}
var latlng; 
if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function (position) {
         latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
    });
}
if(!latlng) {
    latlng = new google.maps.LatLng(57.718, 11.974); 
}

Handeld all my errors :)

watchPosition and getCurrentPosition both accept a second callback which is invoked when there is an error. The error callback provides an argument for an error object. For permission denied, error.code would be error.PERMISSION_DENIED (numeric value 1).

Read more here: https://developer.mozilla.org/en/Using_geolocation

Example:

navigator.geolocation.watchPosition(function(position) {
  console.log("i'm tracking you!");
},
function (error) { 
  if (error.code == error.PERMISSION_DENIED)
     console.log("you denied me :-(");

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM