Drupal 7 ajax page navigation with history

Many clients these days ask for ajax enabled sites for smooth navigation between pages (here's an example http://debbievinick.com). To achieve such behaviour and maintain backwards compatibility with non-javascript browsing (of most interest in that category are search spiders, e.g. Googlebot) the fundamental problem is that for each page you must serve two versions: 1 full page for when you land on the url directly, and 1 ajax version which just returns the content to be updated.

...or do you?

The speed benefits from having ajax navigation don't really come from serving fewer bytes to load page, but from not having to re-render the entire page, and re-parse all your JavaScript and CSS. A simple solution when using a CMS is just to fetch the entire page you want to update the content for, and then extract the section you need.

The following implementation is based on a simple Drupal installation with the Zen theme: http://drupal.org/project/zen

Consider we have 3 pages (/home, /contact, /about) with this basic structure:

<html>
	<head><title><title />...</head>
	<body>
		<div id="header">...</div>
		<div id="mainwrapper">
			<div id="main">
				<div id="content"></div>
				<div id="navigation"></div>
			</div>
		</div>
		<div id="footer">...</div>
	</body>
</html>

The only content changes from page to page is div#main and title

Using jQuery's ajax load function we can specific a part of the HTML tree from the result we want to use by adding a selector:

$('#main-wrapper').load('/about #main');

If run from /home this would fetch the contents of /about, extract the HTML from div#main and overwrite the current contents of div#wrapper with it. This is the bulk of the functionality we require but there are still a few improvements.

Firstly the title of the page is outside div#main and is not updated. This can fixed fairly simply:

var loadPage = function(path) {
	$('#main-wrapper').load(path + ' #main', function(response) {
		var title = response.match(/<title>([^<]+)<\/title>/)[1];
		try {
			$('title').html(title);
		} catch(e) {
			var titleText = $('<div/>').html(title).text();
			document.title = titleText;
		}
	});
};

Setting the title is a little complex because of they way different browsers expect plain text or HTML. You can use whichever technique you prefer.

The other major issue is page state. Click the link to navigate to /about from /home, and then reloading the page would take you back to /home. This is where the history.js library comes in. If you are using Drupal you can drop in the library using this module: http://drupal.org/project/history_js (NB history.js is incompatible with the Overlay module, so you'll have to disable it)

Now we need to do two things: set up the ajax navigation on the main menu links, and pass the state change events through to our loadPage function:

var initMenu = function() {
	$('#main-menu a').each(function() {
		var path = ($(this).attr('pathname').substr(0, 1) != '/') ?
			'/' + $(this).attr('pathname') :
			$(this).attr('pathname');
		$(this).click(function() {
			History.pushState(null, null, path);
			return false;
		});
	});
};
$(document).ready(function() {
	initMenu();
	History.Adapter.bind(window, 'statechange', function() {
		var State = History.getState();
		loadPage(State.url);
	});
});

In older browsers history.js will use hash links to store the page state. If you were to reload a page at /home#./about we can add a bit of code to redirect you to /about :

if(location.hash.substr(0, 3) == '#./') {
	location.href = location.hash.substr(3);
}

We can further optimise by storing loaded page data and titles in memory, so if a page is navigated back to we don't even need to perform an ajax request:

var pageCache = {};
 
var loadPage = function(path) {
	if(pageCache[path]) {
		$('#main-wrapper').html(pageCache[path].body);
		writePage(pageCache[path].title, path);
	} else {
		$('#main-wrapper').load(path + ' #main', function(response) {
			var title = response.match(/<title>([^<]+)<\/title>/)[1];
			pageCache[path] = {
				body: $(this).html(),
				title: title
			}
			writePage(title, path);
		});
	}
};
 
var writePage = function(title, path) {
	initMenu();
	try {
		$('title').html(title);
	} catch(e) {
		var titleText = $('<div/>').html(title).text();
		document.title = titleText;
	}
};

In the final script we add Google Analytics tracking (if you have it) and an option to fade content in and out:

(function($) {
 
	var History = window.History;
	if ( !History.enabled ) {
		return false;
	}
 
	if(location.hash.substr(0, 3) == '#./') {
		location.href = location.hash.substr(3);
	}
 
	var _wrapper = '#main-wrapper';
	var _contents = '#main';
	var _menu = '#main-menu a';
	// set _fade to a selector you want to fade-in/out
	// on page change, e.g. '#content .content'
	var _fade = null;
 
	var initMenu = function() {
		$(_menu).each(function() {
			var path = ($(this).attr('pathname').substr(0, 1) != '/') ?
				'/' + $(this).attr('pathname') :
				$(this).attr('pathname');
			$(this).click(function() {
				History.pushState(null, null, path);
				return false;
			});
		});
	};
 
	var pageCache = {};
 
	var loadPage = function(path) {
		if(_fade) $(_fade).fadeOut('fast');
		if(pageCache[path]) {
			$(_wrapper).html(pageCache[path].body);
			writePage(pageCache[path].title, path);
		} else {
			$(_wrapper).load(path + ' ' + _contents, function(response) {
				var title = response.match(/<title>([^<]+)<\/title>/)[1];
				pageCache[path] = {
					body: $(this).html(),
					title: title
				};
				writePage(title, path);
			});
		}
	};
 
	var writePage = function(title, path) {
		if(_fade) $(_fade).hide().fadeIn('fast');
		initMenu();
		try {
			$('title').html(title);
		} catch(e) {
			var titleText = $('<div/>').html(title).text();
			document.title = titleText;
		}
		if(typeof _gaq != 'undefined') _gaq.push(['_trackPageview', path]);
	};
 
	$(document).ready(function() {
		initMenu();
		History.Adapter.bind(window, 'statechange', function() {
			var State = History.getState();
			loadPage(State.url);
		});
		pageCache[History.getState().url] = {
			body: $(_wrapper).html(),
			title: $('title').html()
		};
	});
 
})(jQuery);

Download here: https://github.com/edg2s/drupal-ajax-navigation

Comments

Any ideas to implement this to views infinite scroll as opposed to menu items?

Thanks a lot for this nice peace of code well documented. I just had to change line 16 of nav.js : 'wrapper': '#main-wrapper', by 'wrapper': '#main', to make to loading works.

Thanks for sharing. Nicolas

You shouldn't need to edit the code file directly, you can pass in a configurations object to init(). See the usage notes on GitHub.

Но цены разных комбинаций для всех слотов.

My website - bottom (www.adhard.ru)

Ушлые манагеры за небольшую мзду
(5-10 тыс.

My web page: играть слоты онлайн
(Trey)

Hello. And Bye.

Hello. And Bye.

Thank you for your post, I look for such article along time, today i find it finally. this post give me lots of advise it is very useful for me.

Всем привет! интересный у вас сайт!
Нашел русскую базу кино: тролли мультфильм 2018 в хорошем качестве hd
Тут: смотреть фэнтези бесплатно в хорошем качестве 720 фэнтези 2018 список 2019
Тут: смотреть фильмы драма онлайн в хорошем качестве http://inspacefilm.ru/drama/ список 2018
Тут: документальные фильмы онлайн в хорошем качестве смотреть документальные фильмы онлайн в хорошем качестве список 2018
Тут: http://inspacefilm.ru/2866-troya-troy-2004.html Смотреть Троя / Troy (2004) онлайн бесплатно
Тут: http://inspacefilm.ru/5892-bratya-donnelli-the-black-donnellys-sezon-1-2...

Всем привет! интересный у вас сайт!
Нашел русскую базу кино: тролли мультфильм 2018 в хорошем качестве hd
Тут: смотреть фэнтези бесплатно в хорошем качестве 720 фэнтези 2018 список 2019
Тут: смотреть фильмы драма онлайн в хорошем качестве http://inspacefilm.ru/drama/ список 2018
Тут: документальные фильмы онлайн в хорошем качестве смотреть документальные фильмы онлайн в хорошем качестве список 2018
Тут: http://inspacefilm.ru/2866-troya-troy-2004.html Смотреть Троя / Troy (2004) онлайн бесплатно
Тут: http://inspacefilm.ru/5892-bratya-donnelli-the-black-donnellys-sezon-1-2...

I got too much interesting stuff on your blog. I guess I am not the only one having all the enjoyment here! Keep up the good work
webnovel

This would be easier if you use WordPress

canadianorderpharmacy.com how safe are canadian online pharmacies
canadian pharmacies shipping to usa http://canadianorderpharmacy.com/
cialis canadian pharmacy http://canadianorderpharmacy.com/#viagra-5mg
drugstore online canada
safe canadian online pharmacies

canadianorderpharmacy.com how safe are canadian online pharmacies
canadian pharmacies shipping to usa http://canadianorderpharmacy.com/
cialis canadian pharmacy http://canadianorderpharmacy.com/#viagra-5mg
drugstore online canada
safe canadian online pharmacies

the photo

Привет всем участникам форума!
Нашел прикольные фотки на этом сайте: http://limonos.ru :
фото природы летом природа лес фото
дизайн дома http://limonos.ru/dizajn/
http://limonos.ru/3447-otkrovenno-provalnye-filmy-s-oshelomlyayuschimi-s... Откровенно провальные фильмы с ошеломляющими сборами по миру
Живая планета Живая планета
http://limonos.ru/4016-neskolko-ceplyayuschih-citat-vladimira-vysockogo....

Привет всем участникам форума!
Нашел прикольные фотки на этом сайте: http://limonos.ru :
фото природы летом природа лес фото
дизайн дома http://limonos.ru/dizajn/
http://limonos.ru/3447-otkrovenno-provalnye-filmy-s-oshelomlyayuschimi-s... Откровенно провальные фильмы с ошеломляющими сборами по миру
Живая планета Живая планета
http://limonos.ru/4016-neskolko-ceplyayuschih-citat-vladimira-vysockogo....

Приветствую всех! класный у вас сайт!
Нашел класную базу кино: смотреть лучшие новинки фантастики 2018
Тут: смотреть лучший семейный фильм онлайн бесплатно семейный фильм онлайн бесплатно в хорошем качестве рейтинг 2019
Тут: русские сериалы лучшее смотреть онлайн бесплатно 2019 лучшие сериалы россии смотреть онлайн бесплатно рейтинг 2019
Тут: хороший триллер бесплатно смотреть триллеры список лучших список 2018
Тут: http://kinovalenok.tv/2940-i-v-gore-i-v-radosti-to-dozhd-to-solnce-saran... Смотреть И в горе, и в радости / То дождь, то солнце / Saranghanda, Saranghaji Anneunda / Come Rain Come Shine (2010) онлайн бесплатно
Здесь: Гильдия художников-постановщиков представила своих номинантов (сериалы) Гильдия художников-постановщиков представила своих номинантов (сериалы)

Sometimes it is difficult to make a good classification of erotic videos and porn videos, and even know if erotic and sensual videos are part of porn, and in iPorno we firmly believe that yes, they are a genre, and of great value, that's why a porn tube like ours we bring this category, where you will not find simple insinuations or assumptions of things that happen is a full-fledged porn video with relax massage, blowjob, fucked, anal if the scene requires it and cumshot, but of course that from another point of view that the bulk of porn, just as is the hardcore or the bizarre, here erotic videos have their place and we want you to enjoy it in style.
The XXX erotic videos of hollyporno adultpornmovie xxndx pornminutes xxxvideotuber sluttyteensex freexxxstuf freexvideotube fastmobiporn sextubesvideos toutpornxxx sextresss hotmomteenxxx sexmagxxx pornotrixxx will not leave you indifferent, especially when you can enjoy them and see them all for FREE!

Hello!
My name is Yelena. Engaged in the creation of cute and beautiful dolls Blythe (Blythe)
I want to offer to your judgment these creatures pleasing the eye. All the details on my website blythedom.com

- Will arrive nicely and securely packed
- And one more important point - the doll is not for active children's games !!! (Or rather, the purchase for children is at the discretion of the parents :). Yet more for aesthetics and for girls over :)))
- A heavy doll with its own eye mechanism and the ability to switch 4 pairs of eyes.
- Body hinged !!! All clothes in miniature 1/6 I sew exclusively by myself. Boots I sew by hand myself.
- Eyes glass with the effect of flicker, I also make myself.
-Make carving (sawed nose, lips, cheeks, chin). Raised "look" made "sleeping eyelids." Replacing eyelashes with realistic ones. Make is made by prof. Pastel, fixed by special means.
- Will arrive in the outfit that is presented on the doll.
More photos on request. ((
I never use photoshop for photo editing! However, the color of the product may differ slightly due to the difference in the color rendition of the monitor of your devices !!!

Привет всем участникам форума!
Нашел класную тему на этом сайте: http://limonos.ru :
красивые девушки с большими красивые лица девушек
природа область фото http://limonos.ru/priroda_foto/
http://limonos.ru/8756-turisty-riskuyut-zhiznyu-radi-effektnogo-selfi.html Туристы рискуют жизнью ради эффектного селфи
http://limonos.ru/5328-ogon-i-loshadi-den-svyatogo-antonio-2016.html

Привет всем участникам форума!
Нашел класную тему на этом сайте: http://limonos.ru :
красивые девушки с большими красивые лица девушек
природа область фото http://limonos.ru/priroda_foto/
http://limonos.ru/8756-turisty-riskuyut-zhiznyu-radi-effektnogo-selfi.html Туристы рискуют жизнью ради эффектного селфи
http://limonos.ru/5328-ogon-i-loshadi-den-svyatogo-antonio-2016.html

Add new comment