responsive
Source: Am I Responsive?

So I was working on a responsive wedding website last week, and one of the requirements was an inline google map with important locations plotted on it. The requirements also stated that I provide a key with info about each, and a link for the full directions, and the style needed to match the look of the site. This is how I tackled it, and made the sucker responsive.

First I toyed with the idea of the static maps api, but the allure of a fully interactive map was much too enticing. After a bit of research, it looked doable, and it was! I’ll run you through each piece individually, but if you’d rather just grab the code and check it out, here is the demo.

The HTML

<section>
  <div class="map" id="map"></div>
  <ul class="mapinfo"></ul>
</section>

I included both jQuery and Lo-Dash on the site, so dumping templates into the DOM seemed like a no brainer. I just needed some simple elements for both the map, and the map info. We’ll get to dumping in the templates in a bit.

The JSON

{
  // Lat/Long on page load
  "mainLatitude": 43.845997,
  "mainLongitude": -70.100412,

  // Default map zoom level
  "zoom": 12,

  // Width/Height of map markers
  "markerWidth": 45,
  "markerHeight": 49,

  // This data will be passed to each marker object
  "markers": [
    {
      "title": "Awesome Place",
      "latitude": 43.828645,
      "longitude": -70.078011,
      "image": "me"
    }
  ],

  // Map look and feel
  "styles": [
    {
      "featureType": "transit",
      "elementType": "geometry",
      "stylers": [
        { "color": "#a1d5b2" }
      ]
    }
  ]
}

I realized early on that I would need an easy way to configure this map. There were a good many customized properties, and JSON fit the bill. I’ve dropped in comments above explaining what each property is for. You can see the full demo JSON here.

One thing to note is the styles array above. This is where I was able to draw out the map with the appropriate styles I needed, so it didn’t look like an eye sore. Google provides a nifty tool for styling called Google Maps API Styled Map Wizard. You can tweak the look and feel directly on a sample map, and generate both JSON data or a static map based on your settings.

The Javascript

$.getJSON('map.json', function ( data ) {
  // All code here
});

First, we’ll grab the json file with the map configuration. We’ll use jQuery’s getJSON for this. All of subsequent map code will live inside the callback function that runs on success. Our json data is stored in the data var we pass into the function.

var myOptions = {
      zoom: data.zoom,
      center: new google.maps.LatLng(data.mainLatitude, data.mainLongitude),
      mapTypeId: google.maps.MapTypeId.ROADMAP,
      panControl: false,
      styles: data.styles
    },
    map = new google.maps.Map(document.getElementById('map'), myOptions);

Next, we’ll setup our options for our Google Maps object. As you can see, most of the options are filled with the values from our data var. This gives us the flexibility of changing the JSON in the future for quick updates to the map. Once our options object is stored in a var, we pass that var into the out of the box Map function, along with the element to drop the map into. If you’re following along, you should now have a map showing up on your page!

A bit on the options above; When creating a new map object, the only required properties are zoom, center, and mapTypeId. I am also turning off the pan controls, and finally passing in our styles we setup earlier.

There are a ton of options with the Maps Javascript API. You can check out the full monty here.

$.each(data.markers, function ( i, markerData ) {
  var markerImage = {
        url: markerData.image + '.png',
        scaledSize: new google.maps.Size(data.markerWidth, data.markerHeight),
        origin: new google.maps.Point(0,0),
        anchor: new google.maps.Point(data.markerHeight/2, data.markerHeight)
      },
      markerOptions = {
        map: map,
        position: new google.maps.LatLng(markerData.latitude, markerData.longitude),
        title: markerData.title,
        animation: google.maps.Animation.DROP,
        icon: markerImage,
        optimized: false
      },
      marker = new google.maps.Marker(markerOptions);
});

Now that we have a map, let’s plot some markers on it. First, we’ll loop through each of the markers in our data var. Then, using the same data object syntax above, we’ll setup options for our marker image, and after that, our marker itself. We’ll then dump that var into the Marker function, which should plot them on our map.

To be honest, the only property you really need here is position. That should give you a simple, out of the box Google-esque marker. But come on, who wants that! Let’s make ours look how we want, and make it look crisp on any device.

I won’t go through everything above, but a few things to note:

  • scaledSize: This is important, as I’m using this to scale our image to 50% of the size, in order to keep it crisp on retina devices. You can read more on the technique here.
  • optimized: Continuing on the HiDPI vein, it is important to set this value to false. This causes the marker images to render as separate requests. A small performance hit, but this allows the images to scale correctly and look sweet on your new retina iPad.
markerTemplate = _.template('<li style="background-image: url(' + markerData.image + '.png)"><h4><%= title %></h4>&lt;a href="http://maps.google.com/maps?daddr=<%= latitude %>,<%= longitude %>" target="_blank"&gt;Directions&lt;a&gt;</li>', markerData);
$('.mapinfo').append(markerTemplate);

Ok, our map is rocking and rolling. Let’s add the final touch of a key, to explain what’s going on with this map, and provide some links that will open in your Google Maps app.

This is where our Lo-Dash templates come in. We’ll add the above code into the same each function as the previous step, so we are appending a template for every marker setup in the JSON. Basiclly, we just pass the marker data to each template, and store it all in a var. We then just append that template var to the mapinfo element, and viola! We’ve got a list of data and some links.

The CSS

section {
  max-width: 700px;
  margin: 0 auto;
  padding: 0 1em;
}

A few notes on the CSS; First, our container section gets some left and right padding. This keeps users from getting stuck in the embedded map on a small screen, or as Trent Walton puts it:

Unless you channel all your swiping mojo, you won’t be able to scroll to the bottom of the page.

The padding gives the user some open lanes to swipe in, so they can scroll past the map, even if they are low on swiping mojo.

.map {
  height: 0;
  padding-bottom: 56.25%;
  padding-top: 30px;
}
@media (min-width: 44.375em) {
  .map {
    width: 60%;
    float: left;
  }
}

Finally, we’ll use some simple styles to get our map to work in a fluid layout. Since the embedded map is absolutely positioned, we will an offshoot of the intrinsic ratio concept. Essentially, the bottom padding creates the space necessary for the fluid width map.

We’ll then add in a media query to float the map left on larger screens, so we don’t just have an insanely large map in your face.

Roundup

Well, thats’s about it. You can view the entire code in the demo below, or check our the Gist on Github. Let me know your thoughts!

Full Demo | Gist