Custom infowindow with jQuery UI Tabs using infobox, Google Maps API v3

Recently, I started using Google Maps API v3 and one of the things I wanted to achieve is to customize the default infowindow, re-skin it and add some tabbed content into it.

After some R&D I found infobox JS which allows you to customize the default infowindow. You can add styles and any HTML content. In a way it actually replaces the default infowindow functionality.

So in this tutorial I’m going to explain how I’ve used Google Maps API v3, jQuery UI Tabs and Infobox JS to achieve the below:

Prerequisites: Knowledge on XHTML / HTML5, CSS, JSON, Good understanding of JS and jQuery is required.

More Tutorials on Google Maps API V3: I’ve recently put up a tutorial on Get Directions and Custom Directions Panel.

HTML5 Mark-up:

<!DOCTYPE html>
<html>
  <head>

    <title>Google Maps API V3: Custom Infowindow using Infobox</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

    <link rel="stylesheet" href="jquery-ui-1.9.0.custom/css/ui-lightness/jquery-ui-1.9.0.custom.min.css" type="text/css" media="all" />

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>    
    <script type="text/javascript" src="jquery-ui-1.9.0.custom/js/jquery-ui-1.9.0.custom.min.js"></script>

    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=&sensor=false"></script>
    <script type="text/javascript" src="infobox.js"></script>

    <style type="text/css">
    	body {margin: 0; padding: 0; width:100%; height: 1000px; overflow: hidden}
    	#map_canvas {margin:0 auto; width: 100%; height: 90%}
		#tabs {font-size: x-small; width: 255px; height: 150px}
		#tabs .ui-tabs-nav {height: 25px}
		p {font-size: x-small; marign: 0; padding: 0}
    </style>

  </head>
  <body>
    <div id="map_canvas"> </div>
    <script type="text/javascript" src="sample.js"></script> 
  </body>
</html>

Google Maps API v3 Basics:

Reference Javascript script:

You need to add the below reference Javascript script to your page. There is no need of API key to use Google Maps API v3. Sensor parameter is false by default. If your device have a GPS sensor make the sensor parameter true(sensor=true).

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

Creating a Map:

To initialize the Map, we need to call constructor of Map class. It takes 2 arguments one is reference of HTML element to insert map (<div id=”map_canvas”> </div>) and two is Map options object.

map = new google.maps.Map(mapCanvas, {
	zoom: 10,
	center: new google.maps.LatLng(-33.950198, 151.259302),
	mapTypeId: google.maps.MapTypeId.ROADMAP 
});

zoom: It is a number to set initial zoom level of the map
center: It sets center of the map by taking coordinates of latitude and longitude.
mapTypeId: It specifies initial type of map. It is an identifier of google.maps.MapTypeId class.

Before continuing further please go through the Google MAPS API v3 Tutorials and get familiarized with the default options and functionalities.

Customizing infowindow using infobox JS:

The objective of this JS is to access the JSON Data and setup the markers using the latitude and longitude on the Google Map and add the custom infobox with jQuery UI Tabs for each of the markers.

For this tutorial I’ve used a JSON file which contains the location data(latitude and longitude). I’ve used a Singleton JS pattern coding standard for the JS. You can read more about this pattern here: The Singleton Pattern – Addy Osmani

Below is the JS code:

var myCustomMaps = (function (jQuery) {
	var instance, 
		map,
		mapCanvas = jQuery('#map_canvas')[0], 
		init = function () {
			return {
				pageLoad: function () {
					this.invokeMyMaps();										
				},	
				invokeMyMaps: function() {	

					var	markers = [], infoBubbles = [], infoBubble, 
						contentString=['<div id="tabs">','<ul>','<li><a href="#tab-1"><span>Tab1</span></a></li>','<li><a href="#tab-2"><span>Tab2</span></a></li>','<li><a href="#tab-3"><span>Tab3</span></a></li>','</ul>','<div id="tab-1">','<p>Tab 1 content. You can customize it further and make this dynamic / JS Object</p>','</div>','<div id="tab-2">','<p>Tab 2 content</p>','</div>','<div id="tab-3">','<p>Tab 3 content</p>','</div>','</div>'].join('');

					map = new google.maps.Map(mapCanvas, {
						zoom: 10,
						center: new google.maps.LatLng(-33.950198, 151.259302),
						mapTypeId: google.maps.MapTypeId.ROADMAP
					});				

					jQuery.ajax({
						url: 'Beach.json', 
						type: 'GET',
						dataType: 'json', 
						success: function(D) {

							beachSpecs = D.beaches;
							jQuery.each(beachSpecs, function(k, v) {
								//console.log(v.beachName, v.latitude, v.longitude);

								// Creating / Initiating Markers:
								var marker = new google.maps.Marker({
									position: new google.maps.LatLng(v.latitude, v.longitude),
									map: map, 
									title: jQuery.trim(v.beachName)
								});								

								// Creating / Initiating Infobox					
								infoBubble = new InfoBox({
									 content: contentString, // Tab content gets appended here.
									 disableAutoPan: false, 
									 maxWidth: 0, 
									 pixelOffset: new google.maps.Size(-140, 0), 
									 zIndex: null, 
									 boxStyle: { 
										//background: "url('tipbox.gif') no-repeat", 
										opacity: 1, 
										width: "280px"
									 }, 
									 closeBoxMargin: "0px", 
									 closeBoxURL: "http://www.google.com/intl/en_us/mapfiles/close.gif", 
									 infoBoxClearance: new google.maps.Size(1, 1), 
									 isHidden: false, 
									 pane: "floatPane", 
									 enableEventPropagation: true									
								});

								markers[k] = marker;
								infoBubbles[k] = infoBubble;							

								// Binding Tab Functionality to infowindow
								google.maps.event.addListener(infoBubble, 'domready', function() {
								  jQuery("#tabs").tabs();
								});	

								// Marker Mouse events
								google.maps.event.addListener(marker, 'mouseup', function() {
									$.each(infoBubbles, function(ix, vx) {
										if(ix >; 0) {
											infoBubbles[ix].close();											
										}
									});
									infoBubble.open(map, marker);
								});																						

							});

						}, // success
						error: function (xhr, textStatus, errorThrown) {
							alert("Error: " + (errorThrown ? errorThrown : xhr.status));
						} // error
					}); //ajax																

				} //invokeMyMaps
			};
		};		
	return {
		load: function () {
			if (!instance) {
				instance = init();
			}
			return instance;
		}
	};
})(jQuery);

(function (jQuery) {
	var myMaps = myCustomMaps.load();
	myMaps.pageLoad();
})(jQuery);

All the Map related functionality is wrapped in the invokeMyMaps function. The below code is required to initialize the Google Map API.

map = new google.maps.Map(mapCanvas, {
	zoom: 10,
	center: new google.maps.LatLng(-33.950198, 151.259302),
	mapTypeId: google.maps.MapTypeId.ROADMAP
});

My next steps are to access the JSON File using the jQuery Ajax Call. On Success I’m accessing the JSON data and doing the following:

Setting up the Markers:

The below JS code is for setting up the Markers on the map with the latitude and longitude data from the JSON Object.

// Creating / Initiating Markers:
var marker = new google.maps.Marker({
	position: new google.maps.LatLng(v.latitude, v.longitude),
	map: map, 
	title: jQuery.trim(v.beachName)
});

Read more about markers.

Creating the Infobox:

Infobox is basically an enhanced version of an Infowindow. The below code is for setting up the infobox with options.

// Creating / Initiating Infobox					
infoBubble = new InfoBox({
	content: contentString, // Tab content gets appended here.
	disableAutoPan: false, 
	maxWidth: 0, 
	pixelOffset: new google.maps.Size(-140, 0), 
	zIndex: null, 
	boxStyle: { 
		//background: "url('tipbox.gif') no-repeat", 
		opacity: 1, 
		width: "280px"
	}, 
	closeBoxMargin: "0px", 
	closeBoxURL: "http://www.google.com/intl/en_us/mapfiles/close.gif", 
	infoBoxClearance: new google.maps.Size(1, 1), 
	isHidden: false, 
	pane: "floatPane", 
	enableEventPropagation: true									
});

The content option is where you need to pass your custom HTML Content. I’m passing the jQuery UI Tads code using contentString variable and Infobox gives you a lot of options to play around and do maximum customization.

Read the detailed documentation on Infobox.

Since I have more than one location data I’m storing the marker details and the infobox details in the arrays:

markers[k] = marker;
infoBubbles[k] = infoBubble;

The Below functions are required to put everything together. The jQuery Tab event needs to be attached to the infobox on domready and infobox .open function is responsible for opening up the infowindow.

// Binding Tab Functionality to infowindow
google.maps.event.addListener(infoBubble, 'domready', function() {
	jQuery("#tabs").tabs();
});	

// Marker Mouse events
google.maps.event.addListener(marker, 'mouseup', function() {
	$.each(infoBubbles, function(ix, vx) {
		if(ix >; 0) {
			infoBubbles[ix].close();											
		}
	});
	infoBubble.open(map, marker);
});

This tutorial is still a work in progress. My intention is to just give a basic idea on how we can put things together. Any feedback is welcome. Please comment or email me for any questions.

Click Here to download the complete code.

More Tutorials on Google Maps API V3: I’ve recently put up a tutorial on Get Directions and Custom Directions Panel.

Good Luck!

Advertisements

5 thoughts on “Custom infowindow with jQuery UI Tabs using infobox, Google Maps API v3

  1. hi there, nice code! i have a few changes that could benefit the user:
    – At the var markers = [], infoBubbles = [], infoBubble,contentString (etc etc) I stopped at infobubble and moved the contentstring withing the jason function to use data from the query.

    var markers = [], infoBubbles = [], infoBubble;

    var contentString=[
    ”,
    ”,
    tab1‘,
    tab2‘,
    tab3‘,
    ”,
    ”,
    ‘Tab 1 content. You can customize it further and make this dynamic / JS Object’,
    ”,
    ”,
    ‘Tab 2 content’,
    ”,
    ”,
    ‘Tab 3 content’,
    ”,
    ”].join(”);

    Furtermore i deleted your line wich coordinates to center the map. added the following lines to automatic center/zoom the map based on all markers on the mapsize.

    var instance,
    map,
    bounds = new google.maps.LatLngBounds();
    (etc etc)

    and added the following lines at the end of the Json query end:
    for (index in markers) {
    var data = markers[index];
    bounds.extend(new google.maps.LatLng(v.lat, v.lng));
    }

    var overlay = new google.maps.OverlayView();
    overlay.draw = function() {};
    overlay.setMap(map);
    map.fitBounds(bounds);

    Hope it helps someone else ! Thanks for the awesome code saved me a ton of work!

    • Thanks Danny. Yes that would definitely benefit others. I myself implemented the dynamic zoom and JSON for content in my client project but never got a chance to update it here. I really appreciate you for taking time and doing this 🙂

      • no probs… im staring at a problem wich i dont know why its wrong. It places all the markers, but i ALWAYS get the last record within the json output in the infobubble. Could you stare with me?

        So for short: it places the markers but Always get the same last result from Json even tough its within the each function.
        $.each(mapsSpecs, function(i, item) {

        // Defining infobubble onclick information
        var contentString=
        [
        ”,
        ”,
        Details‘,
        Prices‘,
        Streetview‘,
        ”,
        ”,
        ”,
        ”,
        ”,
        ”,
        ” + item.street + ‘ ‘ + item.housenr + ”,
        ”+ item.postalcode + ‘ ‘ + item.city + ”,
        ”,
        ”,
        ”,
        ”,
        ‘Tab 2 content’,
        ”,
        ”,
        ‘Tab 3 content’,
        ”,
        ”].join(”);

        // Creating / Initiating Markers:
        var marker = new google.maps.Marker({
        position: new google.maps.LatLng(item.lat, item.lng),
        map: map,
        title: jQuery.trim(item.street)
        });

  2. You can cache the JSON data into a global variable and call it separately before opening the infowindow.
    Here is an example. I changed the marker click event which sits inside the ajax call to below:
    jQuery.each(beachSpecs, function(k, l) {

    //code

    google.maps.event.addListener(marker, ‘click’, function() {
    initInfoWindow(k, marker);
    });

    //code
    });

    So you need to pass the index value along with the marker and populate the data from JSON using the index. So I’m passing the index value k (in your code its the ‘i’ that needs to be passed along with the marker) to the below function.

    initInfoWindow = function(key, marker) {
    jQuery.each(infoBubbles, function(ix, vx) {
    if(ix > 0) { infoBubbles[ix].close() }
    });
    // Below I’m calling the data from JSON and setting it as the content for infobox
    infoBubble.setContent(‘<div style=”background:#eee;”>’+beachSpecs[key].beachName+'</div>’);
    infoBubble.open(map, marker);
    }

    Here is the complete code for Download you can play with it and see if it makes sense.

  3. Hello,

    I know this posting is pretty old…. Can you please provide some tips on how I can read data from a XML or JSON file and display it on the Tabs?

    Thanks in advance.

    Lintu

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s