Unobtrusive Ajax Navigation – Version 3

June 15, 2009 13:24 by scibuff

Last week we continued the tutorial on Unobtrusive AJAX Navigation by cleaning up our Javascript code as well as adding functionality which enabled users to bookmark individual pages and use the “Back” and “Forward” browser buttons for navigation. This time, we’ll focus on cleaning up the back-end scripts to make the solution easily maintainable and expandable. Check out the fully functional demo and/or download the files for this tutorial.

There are two equally good approaches to achieve our goal. One is using simple server-side include (SSI) instructions to keep common content in separate files and include them into our pages at the time when it is needed. The second approach is via Object Oriented (OO) PHP. I am a big fan of OO but too often I see it misused by self-pronounced programmers who naively think that throwing OO concepts on everything makes them better coders (nothing could be further from the truth). There is place and time for deciding to “complicate” your file structure but unless you’re about to create a solution of the size of WordPress (for example) you should think really hard about the pro’s and con’s. Unless your intention is to build a robust, easily extendable and maintainable product with support for hundreds of thousands of users, good old procedural scripting will do the job just as well (if not better).

So without much further ado, let us look at the back-end code from the previous projects:

The first thing that is immediately obvious is that the majority of code in “index.php” and “about.php” is identical. Let’s split this code into appropriate files. Create a directory called “inc” and place the following “.htaccess” file in it:

Order Allow,Deny
Deny From All

All this file does is it prevents any sort of (GET, POST, HEAD etc) requests made directly to the /inc directory. Next, we create a file called “header.php” and place the “print_header” function in it:

<?php
 
function print_header(){
?>
 
	...
	<body id="version-1-0">
 
		<!-- WRAPPER - START -->
		<div id="wrapper">
 
			<!-- HEADER - START -->
			<div id="header">
 
			<h2>Unobtrusive Ajax Navigation - Version 3.0</h2>
 
			<ul >
				<li><a href="index.php">Home</a></li>
				<li><a href="about.php">About</a></li>
			</ul>
 
 			<div></div>
 		</div>
 		<!-- HEADER - END -->
 
 		<!-- CONTENT - START -->
 		<div id="content">
 
<?php
}
?>

Similarly, we create “footer.php” and place the “print_footer” function code in it.

 <?php
 
function print_footer(){
?>
 	 	</div>            
 		 <!-- CONTENT - END -->
 
 		 <!-- FOOTER - START -->
 		 <div id="footer">
 	 		 <p>Footer</p>
 	 	</div>
 	 	<!-- FOOTER - END -->
 	 	</div>
	</body>
</html>
<?php
}
?>

Then create another two include files, one for the content of “index.php” and one for the content of “about.php”. Let’s name them “index.inc.php” and “about.inc.php” respectively, and place the “print_content” functions in them, like so

<?php
 
function print_content(){
?>
	<h2>Homepage</h2>
	<p>Lorem ipsum ...</p>
<?php
}
?>
<?php
 
function print_content(){
?>
	<h2>About</h2>
	<p>Lorem ipsum ...</p>
<?php
}
 
?>

Next, create “content.php” and place the code from above the “print_header” function in “index.php” (or about.php) into a newly created “print_page” function, like so

function print_page( $page ){
 
	require_once("inc/header.php");
	require_once("inc/footer.php");
 
	require_once( $page );
 
	// if the page is requested by AJAX
	if ( isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == "true" ){
		print_content();
	}
	else {
		print_header();
		print_content();
		print_footer();
	}
}

Perhaps, this new function requires a bit of explaining: We want to remove all (as much as possible) code duplicity, so create a function which will print a page content, just as we did before (depending on whether the request came directly from a browser or was requesting by our AJAX Javascript code). The only difference is that we use the “$page” argument to tell the function which page needs to be printed. The script will use the “print_content” function from the appropriate file.

Now, whenever you see something resembling

require_once( $content_inc );

your red alert flashing light should immediately go off. This is potentially a huge security risk: if someone could make the value of “$page” to be “.htaccess” or “.htpasswd” the user could read (encrypted) passwords and see other site settings. In the particular use of the construct, it is unlikely (nothing is impossible) that anyone would be able to exploit it. Nevertheless, it is possible that sometime in the future, to extend functionality or for some other purpose, the smallest modification to the code could leave us vulnerable against an attack. In short, the best practice is NEVER to trust the user, especially when a potential security flaw can easily be fixed by restricting the possible value set. Add the following lines before the function declaration:

define("PAGE_HOME", 0);
define("PAGE_ABOUT", 1);
 
define("PAGE_HOME_SSI", "home.inc.php");
define("PAGE_ABOUT_SSI", "about.inc.php");

change the function declaration to

function print_page( $page = PAGE_HOME )

and add the following switch statement:

	switch( $page ){
		case PAGE_ABOUT : {
			require_once( PAGE_ABOUT_SSI );
			break;
		}
		case PAGE_HOME : {
			// fall through to default
		}
		default : {
			require_once( PAGE_HOME_SSI );
			break;
		}
	}

The few extra lines of code is all that is necessary to prevent the possibility of a large headache (at best) in the future.

Now, all that’s left is to use the newly created files and functions in our original scripts. Thus, we go to “index.php” and place the following lines of code in it:

<?php
require_once("inc/content.php");
print_page( PAGE_HOME );
?>

Similarly, in “about.php”

<?php
require_once("inc/content.php");
print_page( PAGE_ABOUT );
?>

We will conclude this tutorial by adding a page called “Contact”. First of all, create a “contact.php” in the main folder and place the following code inside it:

<?php
require_once("inc/content.php");
print_page( PAGE_CONTACT );
?>

Then open the “content.php” inside the “inc” folder and add

define("PAGE_CONTACT", 2);
define("PAGE_CONTACT_SSI", "contact.inc.php");

as well as

		case PAGE_CONTACT: {
			require_once( PAGE_CONTACT_SSI );
			break;
		}

into the switch statement. Then, let’s add the Contact Page to our navigation by placing the following into the “header.php”

<li><a class="ajax-link" href="contact.php">Contact</a></li>

Finally, we need to create a “contact.inc.php” inside the “inc” directory with a “print_content” function:

<?php
 
function print_content(){
?>
	<h2>Contact</h2>
 
	<p>Address Line 1</p>
	<p>Address Line 2</p>
	<p>City, Zip Code</p>
	<p><strong>Country</strong></p>
 
<?php
}
?>

Next time, we will look at creating Unobtrusive AJAX Navigation as a part of a complex product (using OO) as well as making URL more user friendly (although search engine are perfectly capable of indexing links with query parameters there is an elegant way to make links more user friendly).

Meanwhile, check out a fully functional demo of version 3 and/or download the files for this tutorial.

Unobtrusive Ajax Navigation – Version 2

June 8, 2009 09:28 by scibuff

Last week I started a new series about creating unobtrusive Ajax navigation. The first version represented a good start and laid a solid foundation. Nevertheless, it was far from an ideal solution mainly for the lack of some basic functionality and poor organization of both the front- and back-end code.

This tutorial will focus on cleaning up the Javascript code as well as adding functionality necessary to improve the user experience. Check out the demo and/or download all files.

In general, it is always a good practice to wrap all code within a unique namespace to prevent code collisions. The following few lines of code accomplish that:

if ( !com ) { var com = {}; }
if ( !com.scibuff ) { com.scibuff = {}; }
if ( !com.scibuff.dev ) { com.scibuff.dev = {}; }
if ( !com.scibuff.dev.ajax ) {
	com.scibuff.dev.ajax = function(){
		// private fields
		var tmp = {};
		// public fields
		var pub = {};
		return pub;
	}();
}

There are two objects inside the newly created object (com.scibuff.dev.ajax), i.e. tmp and pub. The former will be used for internal methods, variables and constants for which there is no need to have public access, whereas the later will be accessible via the window object, i.e com.scibuff.dev.ajax.

Now let’s take the code from the version 1 Javascpript file and place it into the newly created structure

		tmp.setAjaxLink = function( element, clickEvent ){
			clickEvent.preventDefault();
			var url = element.attr("href");
			tmp.loadPageContent( url, "#content" );
		};
 
		tmp.loadPageContent = function( url, destination ){
			data["page"] = url;
			$( destination ).load( url, { 'ajax': 'true' }, function(){
				$( destination + ' a.ajax-link').click( function( event ) {
					tmp.setAjaxLink( $(this), event );
				});
			});
		}

Since the tmp object is used to encapsulate internal functions we will need a point of entry from outside of the namespace. We use the pub object and adding the following few lines of code:

		pub.init = function(){
			$('a.ajax-link').click( function( event ) {
				tmp.setAjaxLink( $(this), event );
			});
		}

Now we simply call the newly created function once the DOM object is fully loaded like so:

$(document).ready(function () {
	com.scibuff.dev.ajax.init();
});

From user experience perspective one of the most important features missing in the first version was the ability to bookmark a specific page (and share it on social network sites). This functionality can be quite easily achieved using the URL hash to store necessary data.

We basically need two functions, one to parse the hash string into meaningful data object and one to take a data object and format it into a hash string.

	tmp.HASH_VARS_SEPARATOR = ';'
	/**
	 * Parses the url hash string into a vars (JSON) object
	 */
	tmp.getHashVars = function(){
		var url = window.location.href;
		if ( url.match('#') ){
			var hash = url.split('#')[1];
			var hashes = hash.split( tmp.HASH_VARS_SEPARATOR );
			var data = {};
			for ( var i in hashes ){
				if ( hashes[i] != "" ){
					var tuple = hashes[i].split("=");
					data[ tuple[0] ] = unescape(tuple[1]);
				}
			}
			return data;
		}
		return {};
	}
 
	/**
	 * Sets the url hash string using the values from the vars object
	 * @param {Object} vars
	 */
	tmp.setHashVars = function( vars ){
 
		var url = window.location.href;
		if ( url.match('#') ){ url = url.split('#')[0]; }
 
		var hash = "";
		for ( var key in vars ){
			if ( vars[key] ) {
				hash += key + "=" + escape(vars[key]) + tmp.HASH_VARS_SEPARATOR;
			}
		}
		window.location = url + "#" + hash;
	}

Now we can incorporate these two new functions into the existing code. In the tmp.loadPageContent function, add

	var data = tmp.getHashVars();
	data[ pub.PAGE_VAR_NAME ] = url;
	tmp.setHashVars( data );

to change the URL whenever a new ajax request is made. Similarly, in the pub.init function we add

	var data = tmp.getHashVars();
	if ( data["page"] != "" ) {
		tmp.loadPageContent( data[ pub.PAGE_VAR_NAME ], "#content" );
	}

The load the page content from the page specified in the URL hash data when the user first arrives at our site. These few extra lines of code is all that is needed to give users the ability to deep-link and bookmark any page accessible from the Ajax Navigation.

Now we have functionality for updating the URL hash with every AJAX request and loading page content based on the data present in the hash. To finish off this piece of functionality, let’s enabled navigation using browser’s Back and Forward buttons as well as direct input to the browser’s address bar.

We need to create a process to listen to changes in the address bar URL. Unfortunately, browsers do not provide in-built events for the URL change, but we can easily mimic that functionality with a few lines of custom code using jQuery’s trigger and bind methods.

	tmp.URL_CHECK_INTERVAL = 100;
	tmp.internalUrlChange = false;
	tmp.oldURL = window.location.href;
 
	/**
	 * Checks if the window url has changed and if so,
	 * triggers a "change" events
	 */
	tmp.checkURL = function(){
		if ( tmp.oldURL != window.location.href ){
			tmp.oldURL = window.location.href;
			$( window.location ).trigger(
				pub.URL_CHANGED, { url : window.location.href }
			);
		}
	}
 
	/**
	 * This function is executed every time the browser's url
	 * is changed
	 * @param {Object} event
	 * @param {Object} data
	 */
	tmp.urlChangeHandler = function( event, data ){
		if ( !tmp.internalUrlChange ){
			var data = tmp.getHashVars();
			if ( data[ pub.PAGE_VAR_NAME ] != "" ) {
				tmp.loadPageContent( data[ pub.PAGE_VAR_NAME ], "#content" );
			}				
 
		}
		tmp.internalUrlChange = false;
	}
 
	pub.init = function(){
		...
		$( window.location ).bind( pub.URL_CHANGED, tmp.urlChangeHandler );
		setInterval( tmp.checkURL, tmp.URL_CHECK_INTERVAL );
		...
	}

The tmp.checkURL function will check the browser’s URL for changes every 0.1 second. Every time a change is detected, the tmp.urlChangeHandler function will validate the hash data and load the appropriate page content via tmp.loadPageContent. To prevent infinite loops (e.g. user clicks on a navigation link, which changes the hash triggering an AJAX request, which changes the hash, etc…) we need to add the tmp.internalUrlChange variable and set it to false whenever the URL change will be due to changing the hash internally (as opposed to input by the user).

Hopefully after these two tutorials, you can now create a fully functional unobtrusive AJAX navigation for your site(s). The next part of the series will tackle organization of the back-end code and error checking. Meanwhile, check out the demo of this tutorial and/or download the files used in this tutorial.

Unobtrusive Ajax Navigation – Version 1

June 1, 2009 11:12 by scibuff

This is the first post in my new series about creating unobtrusive AJAX navigation for a website using Javascript frameworks. I still remember the first time I saw AJAX in action on the Gmail registration page a while back. It gave users the option to check if a given username was taken, and it did the check asynchronously, i.e. the HTML page did not have to be reloaded.

AJAX navigation is not only cool, it is also fast and saves bandwidth. It can be seen around the web (Gmail or Facebook for example) but many pages are missing a fall-back for users without Javascript, or on mobile devices. Javascript should be used ONLY to enhance the usability of web sites and NEVER to create functionality that does not already exist. In this tutorial (the first of many) I’m going to demonstrate how easy it is to set up unobtrusive Ajax navigation using jQuery.

Check out the demo of this tutorial or download all the files.

First, create a simple web page.

<body id="version-1">
	<div id="wrapper">
		<div id="header">
			<h2>Unobtrusive Ajax Navigation - Version 1</h2>
			<ul class="inline-list navigation-main" >
				<li><a href="index.php" class="ajax-link">Home</a></li>
				<li><a href="about.php" class="ajax-link">About</a></li>
			</ul>
			<div class="clear-both"></div>
		</div>
		<div id="content">
		</div>
		<div id="footer">
			<p>Footer</p>
		</div>
	</div>
</body>

I took the liberty of adding some CSS styles to make the page look nice. The important part is adding ajax-link to the links’ class name. In just a moment, it will be used it to add AJAX functionality to the navigation. As the first step include the jQuery libraries. I prefer to link to the Google code libraries as it not only saves the trouble of downloading/uploading jQuery for every project but the library is likely to already be in your browser’s cache (from other sites).

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>

Next, add the project’s javascript file, I named it ajax-version-1.0.js and placed it into a js directory.

<script type="text/javascript" src="js/ajax-version-1.0.js"></script>

That’s it for the HTML code. Now let’s have a look at the Javascript that actually does all the work for the AJAX navigation.

$(document).ready(function () {
	$('a.ajax-link').click( function( event ) {
		set_ajax_link( $(this), event );
	});
});
function set_ajax_link( el, event ){
	event.preventDefault();
	var url = el.attr("href");
	load_page_content( url );
}
function load_page_content( url ){
	$("#content").load( url, { 'ajax': 'true' }, function(){
		$('#content a.ajax-link').click( function( event ) {
			set_ajax_link( $(this), event );
		});
	});
}

As a rule of thumb, I wrap my code within the document ready block, to ensure that DOM is fully loaded before trying to access its elements.

$(document).ready(function () {});

The next part searches for all a tag elements which has been assigned a className of ajax-link.

$('a.ajax-link').click( function( event ) {
	set_ajax_link( $(this), event );
});

All such elements are then passed into my set_ajax_link function.  Also pass in the click event instance (we’ll see in a second why).

function set_ajax_link( el, event ){
	event.preventDefault();
	var url = el.attr("href");
	load_page_content( url );
}

The first command in the set_ajax_link function stops the default behavior when the user clicks on a link because the desired functionality is to load the content asynchronously instead of navigating to that page. Next, grab the link’s URL using the attr function, and as the last step  load the content from that URL using the load_page_content function.

function load_page_content( url ){
	$("#content").load( url, { 'ajax': 'true' }, function(){
		$('#content a.ajax-link').click( function( event ) {
			set_ajax_link( $(this), event );
		});
	});
}

All that the load_page_content function has to accomplish is to load the content from the URL. This is achieved via jQuery’s load function. Once the content is loaded it will be placed into the div element with id “content”. Using the second argument of the load function the script will send a query parameter named ajax with value set to true along with the asynchronous request. The third argument points to a function that is executed once a response is received from the server. The script will give the AJAX navigation functionality to all links with className of ajax-link in the received response.

Using the HTML code created in the very first step and some new functionality create the homepage (name it index.php).

<?php
 
	// if the page is requested by AJAX
	if ( isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == "true" ){
		print_content();
	}
	else {
		print_header();
		print_content();
		print_footer();
	}
 
function print_header(){
?>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
	<head profile="http://gmpg.org/xfn/11">
		<title>Unobtrusive Ajax Navigation - Version 1</title>
 
		<link rel="stylesheet" href="style.css" type="text/css" media="screen" />
 
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
		<script type="text/javascript" src="js/ajax-version-1.0.js"></script>
 
	</head>
	<body id="version-1-0">
		<div id="wrapper" class="border-module">
			<div id="header" class="border-module">
				<h2>Unobtrusive Ajax Navigation - Version 1.0</h2>
				<ul class="inline-list navigation-main" >
					<li><a href="index.php" class="ajax-link">Home</a></li>
					<li><a href="about.php" class="ajax-link">About</a></li>
				</ul>
				<div class="clear-both"></div>
			</div>
			<div id="content" class="border-module">
<?php
}
 
function print_footer(){
?>
			</div>
			<div id="footer" class="border-module">
				<p>Footer</p>
			</div>
		</div>
	</body>
</html>
<?php
}
 
function print_content(){
?>
	<h2>Homepage</h2>
	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
<?php
}
 
?>

Now HTML content of our index.php is identical to the static HTML page created originally. The difference is that the HTML code is split into three parts by three PHP functions, print_header(), print_footer() and print_content(). There are a few reasons for this separation. Some will be discussed/addressed in future tutorials. One of the reasons becomes clear when we examine the top of the index.php script page.

// if the page is requested by AJAX
if ( isset($_REQUEST['ajax']) &amp;&amp; $_REQUEST['ajax'] == "true" ){
	print_content();
}
else {
	print_header();
	print_content();
	print_footer();
}

Now we can see the reason behind added the ajax query parameter in our load_page_content Javascript function. The script will use it to determine whether the user arrived at the page directly (when the if statement evaluates to false) or the request came from the AJAX request. If the user arrives directly to our index.php page, the script will output the entire HTML code (using the print_header(), print_footer() and print_content() functions), however, the AJAX request will return the content ONLY (you can’t see that content by visiting index.php?ajax=true directly).

As the final step, I created another page and name it about.php. Then I simply copied everything from out index.php over to about.php and change the content of the print_content() function.

function print_content(){
?>
	<h2>About</h2>
	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
<?php
}
?>

The first version on a simple website using unobtrusive Ajax navigation is now finished. It has only to pages (Home and About) but can easily be extended to as many pages as necessary. The main advantage is that if user’s browser does not have Javascript support (or Javascript is disabled), the user will be able to navigate our sites in the conventional manner by simply clicking on the navigation links. Furthermore, the AJAX functionality can be developed independently of other aspects of the sites, i.e. we can have different people working simultaneously on the front-end and back-end.

Nevertheless, this version is quite far from an optimal solution and has many drawbacks:

  • it does not allow users to bookmark the page they are currently viewing
  • there is no support for the browsers Back and Forward buttons
  • it restricts placing the content from asynchronous calls only to the div element with id “content”
  • the PHP code has as many duplicates are there are pages – print_header() and print_footer() are identical across all pages and should be in a single file
  • the Javascript code does not prevent possible conflicts with other scripts which could use functions with same names
  • the are no error checks or error messages
  • no indication of content being loaded
  • etc…

I will address all these issues in future tutorials on this subject. Meanwhile, check out the demo of this tutorial or download all the files.