Geolocation API Get Certified
The Geolocation API lets a web page ask the browser for the
device’s current geographic position. Because this tutorial runs
in a sandbox, the examples use a small mock that behaves like the
real navigator.geolocation object.
Checking for Support
Always test whether the API exists before using it. Older browsers and pages that are not served over HTTPS may not provide it.
What this code does: Checks whether the Geolocation API is available on the current environment.
if ("geolocation" in navigator) {
console.log("Geolocation is supported");
} else {
console.log("Geolocation is not supported");
}
console.log("Always check support before using the API.");
navigator.geolocation.Getting the Current Position
getCurrentPosition() asks for a single reading. It
takes a success callback and an optional error callback.
What this code does: Requests a one-time location and logs the latitude and longitude.
function createMockGeolocation() {
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 25 },
timestamp: Date.now()
};
}
return {
getCurrentPosition(onSuccess, onError) {
// Call immediately so the editor captures the output
onSuccess(makePosition(40.7128, -74.0060));
}
};
}
const geolocation = createMockGeolocation();
console.log("Requesting current position...");
geolocation.getCurrentPosition(
(position) => {
console.log("Latitude:", position.coords.latitude);
console.log("Longitude:", position.coords.longitude);
console.log("Accuracy:", position.coords.accuracy, "meters");
},
(error) => console.log("Error:", error.message)
);
coords + timestamp — same structure the real API returns.latitude, longitude, accuracy (meters).navigator.geolocation.position.coords.Reading Accuracy and Other Fields
The coords object also contains
accuracy (in meters) and, when available,
altitude, speed, and heading.
What this code does: Displays a friendly message that includes both the coordinates and the reported accuracy.
function createMockGeolocation() {
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 25 },
timestamp: Date.now()
};
}
return {
getCurrentPosition(onSuccess) {
onSuccess(makePosition(51.5074, -0.1278)); // London
}
};
}
const geolocation = createMockGeolocation();
console.log("Getting position...");
geolocation.getCurrentPosition((position) => {
const { latitude, longitude, accuracy } = position.coords;
console.log("You are near", latitude.toFixed(4), longitude.toFixed(4));
console.log("Accuracy: ±" + accuracy + " meters");
});
toFixed(4) keeps numbers readable.Passing Options
A third, optional argument configures the request:
enableHighAccuracy asks for a more precise (but
more battery-intensive) reading, timeout sets how
long to wait, and maximumAge allows a recently
cached position to be reused.
What this code does: Passes options such as
enableHighAccuracy, timeout, and
maximumAge to control how the position is obtained.
function createMockGeolocation() {
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 5 },
timestamp: Date.now()
};
}
return {
getCurrentPosition(onSuccess, onError, geoOptions) {
console.log("Requested with options:", JSON.stringify(geoOptions));
onSuccess(makePosition(35.6762, 139.6503)); // Tokyo
}
};
}
const geolocation = createMockGeolocation();
console.log("Requesting high-accuracy position...");
geolocation.getCurrentPosition(
(position) => {
console.log("Latitude:", position.coords.latitude);
console.log("Longitude:", position.coords.longitude);
console.log("Accuracy:", position.coords.accuracy, "meters");
},
(error) => console.log("Error:", error.message),
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
Tracking Movement with watchPosition()
watchPosition() works like
getCurrentPosition(), but keeps calling the success
callback every time the device’s location changes.
What this code does: Continuously tracks the
user’s movement with watchPosition, receiving
updates as the location changes.
function createMockGeolocation() {
let watchIdCounter = 0;
const activeWatches = {};
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 15 },
timestamp: Date.now()
};
}
return {
watchPosition(onSuccess) {
const id = ++watchIdCounter;
activeWatches[id] = true;
let lat = 40.7128;
let lon = -74.0060;
// Fire a few updates immediately so the editor can capture them
for (let i = 0; i < 3; i++) {
if (!activeWatches[id]) break;
lat += 0.001;
lon += 0.001;
onSuccess(makePosition(lat, lon));
}
return id;
},
clearWatch(id) {
delete activeWatches[id];
}
};
}
const geolocation = createMockGeolocation();
console.log("Starting to watch position...");
geolocation.watchPosition((position) => {
console.log(
"Updated position:",
position.coords.latitude.toFixed(4),
position.coords.longitude.toFixed(4)
);
});
Stopping a Watch with clearWatch()
watchPosition() returns a watch ID.
clearWatch(id) stops receiving updates, similar to
how clearInterval() stops a repeating timer.
What this code does: Stops an active watch by
calling clearWatch with the watch ID returned
earlier.
function createMockGeolocation() {
let watchIdCounter = 0;
const activeWatches = {};
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 15 },
timestamp: Date.now()
};
}
return {
watchPosition(onSuccess) {
const id = ++watchIdCounter;
activeWatches[id] = true;
let lat = 40.7128;
let updates = 0;
// Fire updates immediately
while (activeWatches[id] && updates < 5) {
updates++;
lat += 0.001;
onSuccess(makePosition(lat, -74.0060));
}
return id;
},
clearWatch(id) {
delete activeWatches[id];
console.log("clearWatch called – watching stopped");
}
};
}
const geolocation = createMockGeolocation();
let updateCount = 0;
console.log("Starting watch...");
const watchId = geolocation.watchPosition((position) => {
updateCount++;
console.log("Update #" + updateCount, position.coords.latitude.toFixed(4));
if (updateCount >= 2) {
geolocation.clearWatch(watchId);
console.log("Stopped watching after 2 updates");
}
});
clearWatch.clearWatch — forgetting this drains battery.Calculating Distance Between Two Coordinates
A common real-world task is calculating the distance between two latitude/longitude points using the Haversine formula, which accounts for the Earth’s curvature.
What this code does: Calculates the distance in kilometres between two sets of coordinates using the Haversine formula.
function getDistanceInKm(lat1, lon1, lat2, lon2) {
const R = 6371; // Earth's radius in km
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
// New York City to Los Angeles
const distance = getDistanceInKm(40.7128, -74.0060, 34.0522, -118.2437);
console.log("Distance from New York to Los Angeles:");
console.log(distance.toFixed(1), "km");
getCurrentPosition() vs. watchPosition()
| Feature | getCurrentPosition() | watchPosition() |
|---|---|---|
| Number of readings | One | Continuous, until stopped |
| Needs to be manually stopped? | No | Yes — with clearWatch() |
| Typical use case | “Find my location” button | Live map, navigation tracking |
| Battery impact | Low (one-time) | Higher (ongoing) |
Common Mistakes with Geolocation
- Forgetting to provide an error callback, leaving permission denials or failures completely unhandled.
- Calling
watchPosition()without ever callingclearWatch(), continuing to drain battery after the location is no longer needed. - Assuming the API is available without checking — older browsers, and any page not served over HTTPS, may not support it at all.
- Requesting
enableHighAccuracy: trueeverywhere, even when a rough location would be enough — this increases battery usage. - Not planning for the user simply denying the permission prompt, which is a normal, expected outcome.
Best Practices for Geolocation
- Always provide an error callback and show the user a clear message if location access is denied or unavailable.
- Check
"geolocation" in navigatorbefore calling any geolocation method. - Always call
clearWatch()once you no longer need continuous location updates. - Only request
enableHighAccuracywhen precision genuinely matters, to save battery. - Explain to the user why your app needs their location before triggering the permission prompt.
Quick Summary
navigator.geolocation.getCurrentPosition()requests a one-time location reading.watchPosition()keeps calling its callback every time the location changes, and returns a watch ID.clearWatch(id)stops a running watch.- The position object’s
coordsproperty holdslatitude,longitude,accuracy, and other optional fields. - An options object can set
enableHighAccuracy,timeout, andmaximumAge. - Always handle the error callback — permission can be denied or the location can be unavailable.
Try It Yourself
Modify the code below. Try changing the starting coordinates,
making the mock fail by setting fail: true, or
calculating the distance to a different city.
What this code does: A practice exercise that fetches a mock position, handles errors, and computes the distance to another city.
function createMockGeolocation(options) {
options = options || {};
function makePosition(lat, lon) {
return {
coords: { latitude: lat, longitude: lon, accuracy: 10 },
timestamp: Date.now()
};
}
return {
getCurrentPosition(onSuccess, onError) {
if (options.fail) {
onError({ code: 1, message: "User denied Geolocation" });
} else {
onSuccess(makePosition(48.8566, 2.3522)); // Paris
}
}
};
}
function getDistanceInKm(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
const geolocation = createMockGeolocation(); // change to { fail: true } to test error
console.log("Getting current position...");
geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
console.log("Current position:", latitude, longitude);
const distance = getDistanceInKm(latitude, longitude, 51.5074, -0.1278);
console.log("Distance to London:", distance.toFixed(1), "km");
},
(error) => console.log("Error:", error.message)
);
{ fail: true } to test the error path.{ fail: true }.
Tip:
Always pair a success callback with an error callback,
remember to clearWatch() when you no longer need
live updates, and only ask for high accuracy when your feature
genuinely needs it.
Understanding the Geolocation API gives you the building block for location-aware features, and pairs naturally with the Fetch API for sending coordinates to a server or a mapping service.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.