ES2015 and the Geolocation API

in Front-end

You might be familiar with this, but the geolocation Web API allows a user to provide their location to a web application. It’s opt-in, so a user has to “allow” access to their location in order for the application to get it.

The geolocation API is accessible through the navigator.geolocation object. While there are additional functionalities of the API, today I’m going to focus on obtaining the latitude and longitude coordinates through the navigator.geolocation.getCurrentPosition() method.

And in addition to working with the geolocation Web API, we’re going to use the ES2015 version of JavaScript to improve our code. Now for this blog post, I’d recommend a foundation in ES2015 because there are syntactical bits of ES2015 that aren’t covered in detail. Not familiar with the ES2015 version of JavaScript? You can learn ES2015 in our course, ES2015: The Shape of JavaScript to Come — it will get you up to speed in no time!

What We’re Building

In this post, we’re going to build a simple button that will:

  • Display a user’s latitude and longitude (after they allow us to do so)
  • Display a loading state
  • Display an error message, if one is present

Here’s a CodePen of the finished product:

See the Pen Geolocator Class by Code School (@codeschool) on CodePen.

Note: Click through the HTML and CSS tabs to see the basic markup and styles we’ll be using. We won’t cover that in this post, but you can check it out there.

ES2015 Classes

In ES2015, we have the ability to create JavaScript classes — they’re syntactical sugar on top of JavaScript’s existing prototype-based inheritance.

So instead of doing something like the following (pre-ES2015):

var Animal = function( type, name ) {
  this.type = type;
  this.name = name;
};

Animal.prototype.sayHello = function() {
  return 'Hello, my name is ' + this.name + ' the ' + this.type;
};

var animal = new Animal( 'snake', 'Steven' );
animal.sayHello(); // => 'Hello, my name is Steven the snake.'

We can do this in ES2015:

class Animal {
  constructor( type, name ) {
    this.type = type;
    this.name = name;
  }

  sayHello() {
    return `Hello, my name is ${ this.name } the ${ this.type }`;
  }
}

let animal = new Animal( 'snake', 'Steven' );
animal.sayHello(); // => 'Hello, my name is Steven the snake.'

The ES2015 version provides us with:

  • The class keyword
  • The constructor method
  • The functionName() {} function shorthand
  • String interpolation with ${}

Geolocator Class

For our wrapper around the geolocation Web API, we create a Geolocator class:

class Geolocator {
  // ...
}

Constructor

We create a constructor method, which runs upon instantiation of the class:

constructor() {
  if ( this._isGeolocationAvailable() ) {
    this.navigator = navigator;
  } else {
    return 'Your browser does not support geolocation.';
  }
}

Our constructor method calls the _isGeolocationAvailable() method:

_isGeolocationAvailable() {
  return 'geolocation' in navigator ? true : false;
}

This method returns a boolean of true or false, depending on if the string 'geolocation' is in the navigator object.

Note : This will vary depending on the browser you’re using. Check caniuse.com for more information on which browsers are supported.

If _isGeolocationAvailable() returns true, we set an instance variable for navigator. If it returns false, we simply return a message that the current browser doesn’t support it.

Get Current Position

Once we’ve created an instance of the Geolocator class, we can call the getCurrentPosition() method to return the latitude and longitude:

let geolocator = new Geolocator();
geolocator.getCurrentPosition();

But we can’t quite call it this way because the navigator.geolocation.getCurrentPosition() method is an asynchronous call. Instead, we’re going to return a Promise object, which gives us a nice interface for calling our getCurrentPosition() method:

let geolocator = new Geolocator();
geolocator.getCurrentPosition()
  .then( ( coordinates ) => {
    // ...
  })
  .catch( ( error ) => {
    // ...
  });

When we’re calling the getCurrentPosition() method, it’s clear to see that it’s going to do something we have to wait for. When it’s done, we’re going to perform an operation with the value it returns, and then we’re going to catch the error, if there is one.

Now let’s look at the entire implementation of the getCurrentPosition() method:

getCurrentPosition() {
  return new Promise( ( resolve, reject ) => {
    this.navigator.geolocation.getCurrentPosition( ( position ) => {

      let latitude  = position.coords.latitude;
      let longitude = position.coords.longitude;

      resolve( { latitude, longitude } );

    }, () => { reject( 'We could not get your location.' ); } );
  });
}

We’re returning a Promise object, which accepts a function object with two arguments, resolve and reject:

new Promise( function( resolve, reject) {
  // ...
});

Within the function object, we make the asynchronous call to getCurrentPosition() on the navigator.geolocation object. This function accepts a success callback function, and we’re optionally passing in an error callback function as well:

navigator.geolocation.getCurrentPosition(
  function( position ) {
    // ...
  },
  function() {
    // ...
  });

We’re storing the latitude and longitude from the position.coords object and passing them into the resolve() function:

let latitude = position.coords.latitude;
let longitude = position.coords.longitude;

resolve( { latitude, longitude } );

The resolve() function only accepts a single parameter, so we’re passing in a single object containing the latitude and longitude. If there’s an error, we call reject(), passing in the error message:

reject( 'We could not get your location.' );

And that’s all there is to the Geolocator class. Now, let’s talk about the code that deals specifically with the UI portion of the page.

UI-specific Code

Now that we have the ability to get the latitude and longitude from the device, we need to deal with the following:

  • Clicking the ‘Get My Location’ button
  • Providing a loading state
  • Showing the latitude and longitude on the page

We don’t want this code to be a part of our Geolocator class ( Single Responsibility Principle and all that), so we create a ui object:

let ui = {
  // ...
};

Elements

Within our ui object, we create an object called _elements to hold our HTML elements:

let ui = {
  _elements : {
    $geolocator : document.querySelector( '.js-geolocator' ),
    $error      : document.querySelector( '.js-error'),
    $latitude   : document.querySelector( '.js-latitude' ),
    $longitude  : document.querySelector( '.js-longitude' )
  }
};

The _elements object contains:

  • Our ‘Get My Location’ button
  • The error message element (where the error is displayed)
  • The latitude element (where the latitude is displayed)
  • The longitude element (where the longitude is displayed)

Note : The $ prefix is simply a convention to denote that these are HTML element objects. And the js- prefix on the HTML class itself is another convention for classes that are only used as hooks in JavaScript.

Get My Location

Now it’s time to implement the functionality for the button click. We create a getMyLocation() function inside our ui object:

let ui = {
  // ...
  getMyLocation() {
    // ...
  }
};

And within that function, we add a click handler to our button:

getMyLocation() {
  ui._elements.$geolocator.addEventListener( 'click', function( event ) {
    // ...
  });
}

Within our click handler, we set some variables:

let $element = event.currentTarget;
let currentText = $element.textContent;
let geolocator = new Geolocator();

Now we have the following variables:

The element that was clicked (the button, in this case)
The current textContent of the button (‘Get My Location’)
An instance of our Geolocator class

Let’s change our button text to Loading… to give proper feedback:

$element.textContent = 'Loading...';

And then we call our geolocator.getCurrentPosition() method:

geolocator.getCurrentPosition()
  .then( ( coordinates ) => {
    $element.textContent = currentText;
    ui._elements.$latitude.textContent  = `Latitude: ${ coordinates.latitude }`;
    ui._elements.$longitude.textContent = `Longitude: ${ coordinates.longitude }`;
  })
  .catch( ( error ) => {
    $element.textContent = currentText;
    ui._elements.$error.textContent = error;
  });

In the then() block, we do the following:

  • Set the button text back to its original text
  • Set our latitude HTML element’s text to show the latitude
  • Set our longitude HTML element’s text to show the longitude

And, if there’s an error, we do the following:

  • Set the button text back to its original text
  • Show an error message in the HTML element for our error

Again, here’s the finished product you can take a look at.

See the Pen Geolocator Class by Code School (@codeschool) on CodePen.

Wrapping Up

And that’s it! We now have a self-contained Geolocator class that we can use anywhere! We’ve also built a ui object to contain more functionality as our application scales. And with the help of ES2015 and its new features and syntax, we were able to keep our code clean, readable, and reusable. Now go forth and build cool things with ES2015 and the geolocation Web API! And don’t forget to play through our recent ES2015 course, ES2015: The Shape of JavaScript to Come, to get ES2015 under your belt.

Code School

Code School teaches web technologies in the comfort of your browser with video lessons, coding challenges, and screencasts. We strive to help you learn by doing.

Visit codeschool.com

About the Author

Drew Barontini

Front-end Director at Code School. Internet introvert, real-life extrovert, and an obsessive-compulsive developer who diligently rages about spacing, organization, and all-around cleanliness of code (and also anything).

Might We Suggest