Google Maps API, JSON data, & jQuery carousel

Yesterday I had a really successful day with two trial web development projects, the first of which was to create a map that would show markers based on the location of a comment received from a member of a certain program’s audience. The marker is clickable, of course, and the window that opens displays the comment — or comments! If there are multiple comments, then a navigation bar is created so a viewer can scroll through each comment at their leisure.

Here’s what it looks like with really basic styling:
Screen Shot 2014-05-22 at 10.30.36 AM

Wanna see some code?! Because I want to share some code!

Side note: I’m really proud of this work because it feels like just yesterday that I had no idea what I was doing with JavaScript/jQuery. (Anyone use the ‘Timehop’ app? It keeps reminding me, in fact, that one year ago I really didn’t know what I was doing yet.) I know there is still so much more for me to learn, but I’m past the phase where I feel completely freaked out by it, so that’s awesome.

For the Google Maps API, you get some preset styles and scripts to use:

<style type="text/css">
	html { height: 100% }
	body { height: 100%; margin: 0; padding: 0 }
	#map-canvas { height: 100% }
</style>

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=FALSE"></script>

I also added jQuery, of course.

<script src="jquery-1.11.1.min.js"></script>

You put this inside of your <body></body> tags:

<div id="map-canvas"></div>

Then, the stuff that makes the first part of this project work!

Set up some JSON data (this is just a snippet of what I used for the screenshot above). Note that I added a field for the desired marker’s icon so that I can change which icon is used based on the content (if there is one comment or multiple comments).

{
	"memory": [
		{
			"lat": "44.953",
			"lng": "-93.089",
			"title": "Saint Paul, Minnesota",
			"markericon": "http://labs.google.com/ridefinder/images/mm_20_yellow.png",
			"description": "<div class='memory'><blockquote>This is just a sample.</blockquote>Amy</div>"
		}
	]
}

Then work on the actual script. Most of this first bit comes direct from Google as it walks you through set up, though I did set up a few extra variables ahead of time.

<script type="text/javascript">
	var map;
	var infoWindow = new google.maps.InfoWindow();

	var markers = [];
	
	var $Latlng;
	var $title;
	var $contentString;

	function initialize() {
		var saintpaul = new google.maps.LatLng(44.944, -93.093);
		
		var mapOptions = {
			center: saintpaul,
			zoom: 4 // 0 = whole world
		};
		
		map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

The infoWindow is set up initially with blank content, but the JSON data fills it all in.

		infoWindow = new google.maps.InfoWindow({
			content: ''
		});

		var url = 'map_test.json';
		$.getJSON(url, function(data) {
		
			for (i = 0; i < data.memory.length; i++) {
				markers = data.memory[i];
				
				$Latlng = new google.maps.LatLng(markers.lat, markers.lng);
				$title = markers.title;
				$icon = markers.markericon;
				$contentString = '<div id="content" class="slides">' + markers.description + '</div>';

This is when markers are generated and placed:

				var marker = new google.maps.Marker({
					position: $Latlng,
					map: map,
					title: $title,
					icon: $icon
				});

Early on, I was having an issue that each marker's infoWindow was being populated with the content from the last item in the JSON file. This next line (and following function) makes sure each marker/infoWindow combo is populated with its own content!

				bindInfoWindow(marker, map, infoWindow, $contentString);
			}
		});

	}

This next function grabs all of the previously loaded data and sets the content of each of the infoWindows. When a user clicks on a marker, they see the comment (or comments) from that location.

	function bindInfoWindow(marker, map, infoWindow, $contentString) {
		google.maps.event.addListener(marker, 'click', function() {
			infoWindow.setContent($contentString);
			infoWindow.open(map, marker);
		});	
	}

Something that's missing that I still want to try to work on is setting the height of each infoWindow based on the tallest bit of memory (the <div class="memory"> items). I know I would somehow need to reveal each memory somewhere in order to get its height, then hide it again until a user clicks the marker to reveal it. Maybe it's not worth it, but it would be a nice-to-have feature when there are more comments and they have longer lengths.

Following that, I set up a function to create the slides-type of display within the infoWindow for JSON items that have more than one <div class="memory"></div>.

	function initSlides() {
		//create the slideshow nav if there is more than one item in it                
		if ($('.slides div.memory').length > 1) {
			var slidesnavlis = [];
		
			$('.slides div.memory').each(function (index) {
				slidesnavlis.push('
  • '); }); $('.slides').after('
      '); $('.slidesNav').html(slidesnavlis.join('\n')); } //show the first slide when the infoWindow opens $('.slides > div.memory:first, .slidesNav > li:first').addClass('active'); }

      Putting it all together... Get all the data, place some markers, figure out the content for each infoWindow.

      	$(function() {
      		initialize();

      This next line was necessary in order to delay the creation of the slides within the infoWindow until all of the content had been loaded and was ready to be shuffled around a bit.

      		google.maps.event.addListener(infoWindow, 'domready', function() {
      			initSlides();
      
      			// When you click on a nav item, go to that item
      			$('.slidesNav li').on('click', function() {
      				var clickedSlidesNavLi = $('.slidesNav li').index(this);
      				var slidesActiveIndex = $('.slides div.memory.active').index();
      
      				if (clickedSlidesNavLi !== slidesActiveIndex) {
      					// Change the nav elements
      					$('.slidesNav > li.active').removeClass('active').end();
      					$('.slidesNav > li').eq(clickedSlidesNavLi).addClass('active').end();
      
      					// Slide the main slideshow
      					$('.slides > div.memory.active').animate({ left: '-100%' }, 1000).end();
      					$('.slides > div.memory').eq(clickedSlidesNavLi).animate({ left: '0' }, 1000, function () {
      						$('.slides > div.memory.active').removeClass('active').css('left', '100%');
      						$('.slides > div.memory').eq(clickedSlidesNavLi).addClass('active');
      					}).end();
      				}
      			});
      		});
      
      	});		
      </script>

      The only other thing I needed to do was to apply some basic styling to my slides and the navigation. This should be placed in the <head> section either inline or through a stylesheet link.

      <style type="text/css">
      	.slides blockquote {
      		margin: 0 0 1em 0;
      		font-style: italic;
      	} 
      	.slides {
      		width: 300px; 
      		height: 200px; /* could height be reset based on height of tallest memory div? */
      		position: relative;
      		overflow: hidden;
      	}
      	.slides div.memory {
      		width: 96%;
      		height: 100%;
      		padding: 0;
      		margin: 0;
      		list-style: none;
      		position: absolute;
      		left: 100%;
      		top: 3px;
      	}
      	.slides div.memory.active {
      		left: 0;
      	}
      	.slidesNav {
      		padding: 0;
      		margin: 0;
      		list-style: none;
      		position: absolute;
      		bottom: 0;
      		right: 0;
      	}
      	.slidesNav li {
      		height: 16px;
      		width: 17px;
      		background-color: blue;
      		float: left;
      		margin-right: 3px;
      	}
      	.slidesNav li.active {
      		background-color: red;
      	}
      </style>

      And that's all I know so far. Questions or comments? (Or helpful tips if you're a more experience JS/jQuery developer!)

      My next project is a jQuery timeline that loads events from JSON data and places them along a linear field based on their date. I didn't want to use a plugin because (a) I'm stubborn and (b) I wanted to learn more and really understand how the code is working. It's a little more complex, but it's close! I'll share my work when it's more polished and closer to 'final'.

      Leave a Reply

      Your email address will not be published. Required fields are marked *