Utilizing the Geolocation JavaScript API for Serving Alternate Style Sheets – Part 2

At long last, here is part 2 and here be geolocation! Aside from the geolocation API, we will talk about Web Storage (namely: localStorage) and a wee bit of jQuery for a quick AJAX call & fading effects. Finally, we will use some PHP functions for sunrise and sunset time's calculations.

Utilizing the Geolocation JavaScript API for Serving Alternate Style Sheets - Part 2


Part 2

To recap: in part 1, we discussed HTML and CSS and the idea for this part is to locate the visitors of our demo page using the geolocation API and then use PHP to calculate the sunrise and sunset times based on the visitor’s latitude and longitude. If the current time happens to be before sunrise or after sunset, we add a new style sheet which will darken the layout’s colors.

Geolocation Demo

JavaScript – Finding Out Where You Are

You have been patient waiting for this part so let’s go right into it.

The Geolocation API – Getting the Longitude & Latitude Data

As the information provided by the geolocation API is available to JavaScript, we need to call our script file at the bottom of our main document:

<script src="scripts/myScripts.js"></script>

Inside this file, we will write the script which does the following:

  • Pulls the longitude and latitude information;
  • Serves it to a PHP file via AJAX;
  • The PHP file will calculate the sunrise and sunset times for the selected location and return "day" or "night" depending on the current time;
  • In case it is night time, we will fade in the modal dialog where we can choose to append the styles-night.css file and darken the page;
  • If the visitor decides to apply the other style sheet, we append it to the head element and fade out the dialog;
  • In case the visitor chooses not to apply the dark style sheet, we just fade out the dialog;
  • As a bonus, we will use localStorage to remember the visitor's choice (should they want their choice to be remembered).

Before we get the longitude and latitude we should first check if the browser supports geolocation and localStorage. This is the main reason why we loaded the Modernizr library. (Modernizr can check for many more browser capabilities; you can check out the full list in the Modernizr documentation.) If a browser supports a feature, in our case geolocation & localStorage, the script adds geolocation & localstorage classes to the html element of our main document. We could then check if the html element has that class and only then execute the script.

Alternatively, we could write an if statement to see if the browser supports geolocation & localStorage and proceed accordingly. We will be using the if statement; I believe that this is the more elegant way and that utilizing classes is more useful for CSS. For instance, we can check for a class signifying border-radius support and specify a rule for browsers which don't support it by making the no-border-radius class first in the selector stack.

So, our script begins with the following:

if ((Modernizr.geolocation) && (Modernizr.localstorage)) {
	navigator.geolocation.getCurrentPosition(function (position) {
		var latitude = position.coords.latitude,
			longitude = position.coords.longitude,
			dataString = 'lat='+ latitude + '&long=' + longitude;
		
		console.log('latitude=' + latitude + ' & longitude=' + longitude);

		$(function() {
			$.ajax({
				type: "POST",
				url: "calc.php",
				data: dataString,
				cache: false,
				success: function(data){
					console.log('Data Saved: ' + data);
					if (data == 'night') {
						$('#modalOverlay').fadeIn(500);
					}
				}
			});
			$('#dialog ul li a').click(function(e) {
				if ($(this).hasClass('green')) {
					$('head').append('<link href=styles-night.css rel=stylesheet />');
				}
				$('#modalOverlay').fadeOut(300);
				e.preventDefault();
			});
		});
	});
} else {
console.log('bummer, man!');
}

Let's break down the script. The only bit that actually deals with geolocation assuming the browser passes the if checks) is the following:

navigator.geolocation.getCurrentPosition(function (position) {
	var latitude = position.coords.latitude,
		longitude = position.coords.longitude[,]

Here, we are using the position interface to get the visitor’s coordinates, namely: latitude and longitude. (For more information on the position interface and what other data you can get from it, check out the W3C geolocation API specification.) After the call to the geolocation API, we set the latitude and longitude values to the two, oh, so imaginatively named variables.

We also set the value of a third variable, dataString, which we will pass to the PHP file via AJAX.

dataString = 'lat='+ latitude + '&long=' + longitude;

The following line is there for debugging purposes, we echo out the latitude and longitude values to the console:

console.log('latitude=' + latitude + ' & longitude=' + longitude);

This just lets us see the values in the console, a way of making sure we are getting the data. I recommend you remove all console.log commands from your production code.

Hello, jQuery! – AJAX and Effects

If you checked out the source code of the demo, you’ve surely seen that we link to the latest version of the jQuery library before we call our myScripts.js file. We will use jQuery to make writing the AJAX and the effects code easier.

$(function() {
	$.ajax({
		url: "calc.php",
type: "POST",
		data: dataString,
		success: function(data){
			console.log('data returned: ' + data);
			if (data == 'night') {
				$('#modalOverlay').fadeIn(500);
			}
		}
	});
	$('#dialog ul li a').click(function(e) {
		if ($(this).hasClass('green')) {
			$('head').append('<link href=styles-night.css rel=stylesheet />');
		}
		$('#modalOverlay').fadeOut(300);
		e.preventDefault();
	});
});

After the first line which is the statement that waits for the document to be "ready" for manipulation, we call jQuery’s $.ajax function which performs an asynchronous HTTP request:

$.ajax({
	url: "calc.php",
	type: "POST",
	data: dataString,
	success: function(data){
console.log('data returned: ' + data);
		if (data == 'night') {
			$('#modalOverlay').fadeIn(500);
		}
	}
});

We can split the key/value pairs inside the $.ajax function into two groups: the URL we are calling (url) and settings (all other pairs).

  • type - this is the type of request we will be making. In our example, we use POST because we are sending the dataString variable to the PHP file;
  • data - this is the data we are sending, namely: the dataString variable. (Notice how appropriately named these settings are?);
  • success - here we call a function to be executed after a successful request. The data parameter passed to the function is the data we receive back from the PHP file – in this case, it is a string. Inside of the function, we log the returned data to the console and, if the returned data is "night", we fade in the modal dialog.

Next on our agenda is handling the clicks in the modal dialog.

The modal dialog presented during nighttime visits to the site:

Modal Dialog

$('#dialog ul li a').click(function(e) {
	if ($(this).hasClass('green')) {
		$('head').append('<link href=styles-night.css rel=stylesheet />');
		}
	$('#modalOverlay').fadeOut(300);
	e.preventDefault();
});

We target the two links within the dialog with the #dialog ul li a selector and write a little function that will handle the clicks. First, we check if the clicked link has a class attribute with the value green. (Not the best choice for a class name, I know.) This tells us if the visitor wants to enable the darker style sheet. In case they do, we append the following line to the end of the head element:

<link href=styles-night.css rel=stylesheet />

After our little if check, we fade out the modal dialog and prevent the default action once the links are clicked. Here, that means that the # symbol (which is the target of all the dummy links on the demo page) won’t be appended to the URL in the address bar.

localStorage – Wasn't There Something We Should Be Remembering?

There is one more order of business before we move onto the PHP code: using localStorage to store the visitor’s decision to darken the layout. localStorage is part of the Web Storage API and here we will use it as an alternative to cookies. The idea is to store two key/value pairs: one which will tell whether to display the modal dialog and the other one to determine whether to darken the layout.

The first key/value pair will be set when we check whether the visitor clicked on the green or red button inside the modal dialog:

if ($(this).hasClass('green')) {
	$('head').append('<link href=styles-night.css rel=stylesheet />');
	localStorage.setItem('darken', 'yes');
} else {
	localStorage.setItem('darken', 'no');
}

If the visitor clicks on the green button, they want to darken the site and we set the darken key to yes. The syntax is pretty simple; we call the setItem function which takes two parameters, yes: the key and the value.

localStorage.setItem('darken', 'yes');

Alternatively, we need to set the value of the darken key to no:

localStorage.setItem('darken', 'no');

The other key/value pair will be used to decide whether to display the modal dialog and we will place the localStorage code between the line which fades out the dialog and the line that prevents the default on-click action:

$('#modalOverlay').fadeOut(300);
if ($('#rememberBox').is(':checked')) {
	localStorage.setItem('remember', 'yes');
} else {
	localStorage.setItem('remember', 'no');
}
e.preventDefault();

First we determine if the check box in the modal dialog is checked and we get its state at the moment of the click because our code is placed within the function that is executed once the visitor clicks on one of the buttons inside the dialog. So, if a visitor wants his choice to be remembered we set the value of a key we name remember to yes. If the visitor does not wish their choice to be remembered, we set the remember key to no.

Finally we need some more if checks to determine if the visitor was already on the site and to see if they wanted their choice to be remembered.

We wrap everything inside our initial Modernizr checks into the first if check:

if ((localStorage.getItem('darken') == 'yes') || (localStorage.getItem('darken') === null)) {

Opposite of setting key/value pairs with setItem, is the getItem function which takes one parameter, the name of the key and returns its value. Basically, we check for two conditions under which we should execute the whole function:

  1. If this is a returning visitor who decided to darken the site or
  2. If this is a new visitor (or a returning visitor who didn’t want their choice remembered)

If one of these conditions is satisfied we should continue executing the code. Otherwise, the only other option is that they decided not to darken the site and wanted that choice remembered.

The next check goes into the success function of our AJAX call:

if (data == 'night') {
	if ((localStorage.getItem('remember') == 'no') || (localStorage.getItem('remember') === null)) {
		$('#modalOverlay').fadeIn(500);
	} else if (localStorage.getItem('darken') == 'yes') {
		$('head').append('<link href=styles-night.css rel=stylesheet />');
	}
}

First, we want to see if our visitor was here before and decided not to have his choice remembered or we have a new visitor. If that is the case, we fade in the modal dialog. Otherwise, we check if the visitor previously chose to darken the layout. If so, we append the alternate style sheet to the head element.

Final note before we move onto PHP: if you need to remove a localStorage key, use the removeItem() function – it takes one parameter, the name of the key.

PHP – Calculating the Sunrise and Sunset Times

This last part will be very short as our calc.php file doesn't contain much code.

<?php
$latitude = $_GET['lat'];
$longitude = $_GET['long'];

$currentTime = date("H:i");

$sunrise = date_sunrise(time(), SUNFUNCS_RET_STRING, $latitude, $longitude, 96, 1);
$sunset = date_sunset(time(), SUNFUNCS_RET_STRING, $latitude, $longitude, 96, 1);

if (($currentTime > $sunrise) && ($currentTime < $sunset)) {
	echo 'day';
} else {
	echo 'night';
}

First off, we get (using $_GET, yes) the latitude and longitude values that we passed to the PHP file with AJAX. Next, we set a variable $currentTime to the current time by calling the date function and passing the H:i parameters. This formats the time in the 24-hour format with leading zeroes (full set of parameters can be found in the PHP manual for the date function).

Now it’s time to calculate the sunrise and sunset times. For this, we will use the date_sunrise and date_sunset functions. Both functions accept the following parameters:

  • timestamp – this is the timestamp of the day for which we want to calculate the sunrise and sunset times so we just use time();
  • format –the format in which we will get the output and it has three possible values:
    • SUNFUNCS_RET_STRING (returns the time as string: 16:46, and this is what we want);
    • SUNFUNCS_RET_DOUBLE (returns the time as float: 16.78243132) and
    • SUNFUNCS_RET_TIMESTAMP (returns the time as integer (timestamp): 1095034606).
  • latitude – this is obviously the latitude and we use the $latitude variable;
  • longitude – this one gets the $longitude variable;
  • zenith – this is the angle that the sun makes with a line perpendicular to the Earth’s surface. For "civilian" twilight a value of 96 is recommended;
  • gmtoffset – the offset from the Greenwich Mean Time.

Once we get our sunrise and sunset times back, we compare them with the current time:

if (($currentTime > $sunrise) && ($currentTime < $sunset)) {
	echo 'day';
} else {
	echo 'night';
}

In case it’s after sunrise and before sunset, we echo out day. Otherwise, we echo out night. Simple enough. When “night” is returned, our JavaScript will do its magic and we can have a darker layout.

Conclusion

So, there it is. I hope that the article wasn’t too convoluted to follow and I hope you enjoyed reading it. Thank you for reading and, as always, I look forward to any questions and comments.