π Geolocation API
"Show me stores near me." "Fill in my address." "Where's my delivery?" Behind each of those is one small browser API. The Geolocation API lets a web page read the user's coordinates β only with explicit permission β and turn them into maps, distances, and local content.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Request a location once with
getCurrentPositionand continuously withwatchPosition - Read the Position object β latitude, longitude, accuracy, speed, and heading
- Explain the permission model and why HTTPS is mandatory
- Handle all four error codes gracefully with fallbacks
- Calculate distance between two points and track location without draining the battery
Estimated Time: 35β45 minutes β’ Difficulty: Intermediate
Hands-on: Build a "How far am I?" tool that measures the distance from you to a landmark.
In This Lesson
What the Geolocation API Does
The Geolocation API exposes a single object, navigator.geolocation, that answers one question: where is this device right now? The browser figures that out by blending several signals and reporting the best estimate it can:
- GPS β most accurate (a few metres), needs hardware, works best outdoors
- Wi-Fi positioning β good in cities and indoors, matches nearby networks to a database
- Cell-tower triangulation β wide coverage, lower accuracy
- IP address lookup β the rough fallback, often only city-level
You don't choose the method β you ask for a position and (optionally) hint at how much accuracy you want. It's like a rideshare app: it just knows where you are, quietly switching between satellites and Wi-Fi as conditions change.
Permissions & HTTPS
Location is sensitive personal data, so the API is gated by two hard rules:
- Explicit consent. The first request triggers a browser permission prompt. The user can allow once, allow always, or block β and revoke later in site settings.
- Secure context. Geolocation only works over HTTPS (or
localhostfor development). On plainhttp://the request fails immediately.
π Check permission without prompting
The Permissions API lets you see the current state first, so you can tailor your UI: navigator.permissions.query({ name: 'geolocation' }) resolves to { state: 'granted' | 'denied' | 'prompt' }.
π‘ Ask at the right moment. Don't demand location on page load β users reflexively block prompts they don't understand. Request it right after a clear action like tapping "Find stores near me," when the reason is obvious.
Getting Position Once
getCurrentPosition takes a success callback, an optional error callback, and an optional options object. Here's the modern promise-wrapped version, which reads much more cleanly with async/await:
// Wrap the callback API in a promise
function getPosition(options) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
async function showLocation() {
if (!('geolocation' in navigator)) {
console.warn('Geolocation is not supported');
return;
}
try {
const position = await getPosition({
enableHighAccuracy: true, // prefer GPS
timeout: 8000, // give up after 8s
maximumAge: 0 // no cached position
});
const { latitude, longitude, accuracy } = position.coords;
console.log(`You are at ${latitude}, ${longitude} (Β±${accuracy} m)`);
} catch (err) {
handleLocationError(err);
}
}
π The three options
enableHighAccuracy β true asks for GPS-grade precision (more battery); false accepts network positioning.
timeout β milliseconds to wait before failing with a TIMEOUT error.
maximumAge β how old (ms) a cached position may be before a fresh one is required. 0 forces a new reading.
The Position Object
A successful call hands you a GeolocationPosition with a coords object and a timestamp. Some fields are null when the device can't supply them.
| Property | Meaning | Everyday analogy |
|---|---|---|
coords.latitude | Northβsouth position (decimal degrees) | How far up/down the map |
coords.longitude | Eastβwest position (decimal degrees) | How far left/right the map |
coords.accuracy | Radius of confidence in metres | The "Β±X m" margin of error |
coords.altitude | Height above sea level (or null) | Which floor you're on |
coords.heading | Direction of travel, 0β360Β° (or null) | Which way the car points |
coords.speed | Velocity in m/s (or null) | How fast you're moving |
timestamp | When the fix was taken (ms epoch) | The photo's time stamp |
β οΈ Always respect accuracy
A desktop on Wi-Fi might report accuracy: 30000 β a 30 km radius. Show the margin, or don't pin an exact map marker on data that vague. altitude, heading, and speed are frequently null, so guard before using them.
Handling Errors
The error callback receives a GeolocationPositionError whose code matches one of four constants. Handle each with a distinct, helpful response:
function handleLocationError(error) {
switch (error.code) {
case error.PERMISSION_DENIED: // 1
showMessage('Location blocked. You can enter your city manually.');
offerManualEntry();
break;
case error.POSITION_UNAVAILABLE: // 2
showMessage('Could not determine your location right now.');
break;
case error.TIMEOUT: // 3
showMessage('Location request timed out β try again.');
break;
default: // unknown
showMessage('An unexpected error occurred.');
console.error(error);
}
}
The most common code by far is PERMISSION_DENIED. Treat it as a normal path, not a crash: every location feature should degrade to a manual address input so blocked users aren't stuck.
Continuous Tracking & Battery
For live navigation or a running tracker, watchPosition fires your callback every time the location updates. It returns a watch ID you later pass to clearWatch to stop.
const watchId = navigator.geolocation.watchPosition(
(position) => updateMap(position.coords),
handleLocationError,
{ enableHighAccuracy: true, maximumAge: 1000, timeout: 10000 }
);
// Stop tracking when the feature is closed β critical for battery
function stopTracking() {
navigator.geolocation.clearWatch(watchId);
}
π Watch the battery
enableHighAccuracy: true keeps the GPS radio hot and drains power fast. For casual features use network positioning (false) and a generous maximumAge. Always clearWatch when the user leaves the view β a forgotten watch runs until the tab closes.
Measuring distance: the Haversine formula
Coordinates alone aren't useful until you can compute the distance between two of them. The Haversine formula gives great-circle distance across the Earth's surface:
// Distance in metres between two lat/lng points
function distanceMeters(lat1, lng1, lat2, lng2) {
const R = 6371e3; // Earth's radius in metres
const toRad = (d) => d * Math.PI / 180;
const dPhi = toRad(lat2 - lat1);
const dLambda = toRad(lng2 - lng1);
const a = Math.sin(dPhi / 2) ** 2 +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(dLambda / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
Hands-on: Distance Tool
ποΈ "How far am I from the Eiffel Tower?"
Objective: Read the user's location and report how far they are from a fixed landmark, in both kilometres and miles.
Starter HTML
<button id="measure">Measure distance</button>
<p id="result" aria-live="polite"></p>
Your tasks
- Define the target: Eiffel Tower =
{ lat: 48.8584, lng: 2.2945 }. - On click, request the user's position (handle the "no permission" case).
- Use the Haversine function above to compute metres, then display km (Γ·1000) and miles (Γ·1609.34).
- Bonus: also show the reported accuracy so the user knows the margin.
π‘ Hint
Reuse the getPosition promise wrapper from earlier. Round your output with .toFixed(1) so you don't print 12 decimal places.
β Solution
const TARGET = { lat: 48.8584, lng: 2.2945 }; // Eiffel Tower
const result = document.getElementById('result');
function getPosition(opts) {
return new Promise((res, rej) =>
navigator.geolocation.getCurrentPosition(res, rej, opts));
}
document.getElementById('measure').addEventListener('click', async () => {
result.textContent = 'Locatingβ¦';
try {
const pos = await getPosition({ enableHighAccuracy: false, timeout: 8000 });
const { latitude, longitude, accuracy } = pos.coords;
const meters = distanceMeters(latitude, longitude, TARGET.lat, TARGET.lng);
const km = (meters / 1000).toFixed(1);
const miles = (meters / 1609.34).toFixed(1);
result.textContent =
`You are ${km} km (${miles} mi) from the Eiffel Tower β location accurate to Β±${Math.round(accuracy)} m.`;
} catch (err) {
result.textContent = err.code === err.PERMISSION_DENIED
? 'Location permission is needed to measure distance.'
: 'Could not get your location. Please try again.';
}
});
Grant permission and you'll see your real distance to Paris. Deny it and the tool tells you why instead of failing silently.
Best Practices
β Do
- Request location in response to a user action, with the reason clearly visible.
- Always provide a manual-entry fallback for denied or unsupported cases.
- Display or account for
accuracyβ never treat a fuzzy fix as precise. - Serve over HTTPS and feature-detect
'geolocation' in navigator.
π« Don't
- Prompt for location on first load with no context.
- Leave
watchPositionrunning after the user moves on. - Default to
enableHighAccuracy: truewhen city-level is enough. - Assume
altitude,heading, orspeedare present.
Summary & Quiz
π Key Takeaways
navigator.geolocationoffers getCurrentPosition (once) and watchPosition (continuous).- Location requires explicit permission and a secure (HTTPS) context.
- The Position object carries latitude, longitude, and an all-important accuracy radius.
- Handle the four error codes β especially
PERMISSION_DENIEDβ with a manual fallback. - High accuracy costs battery; stop watches with
clearWatch, and use Haversine for distances.
π― Quick Quiz
Question 1: Your geolocation feature works on localhost but fails once deployed to a plain http:// site. Why?
Question 2: Which option should you set to true only when you truly need GPS-grade precision, because it drains the battery?
Question 3: A user taps "Block" on the permission prompt. What's the best response?
π Further Reading
π What's Next?
You can now read where the user is. Next we tackle a different kind of direct manipulation: the Drag and Drop API, which lets users grab elements and files with the cursor to build sortable lists, kanban boards, and upload zones.
π Nice work!
Location-aware features are now in your toolkit β used responsibly, with permission and a fallback.