AJAX: How To Handle Bookmarks and Back Buttons, Advanced Example
AJAX: How To Handle Bookmarks and Back Buttons, Advanced Example
To begin, let's see what our application will look like when finished. We
will create an AJAX web page with a series of topic links; when topics
are selected, their contents will be remotely loaded and shown on the
right-hand side of the page without performing a full page refresh:
As users select topics in the left hand menu of our sample application,
we fetch the topic's contents in the background and use the
DhtmlHistory API to record bookmarkable history. When the page is
initially loaded, we retrieve the list of available topics from an XML file
and cache them locally using the HistoryStorage API, using this XML file
to build up our user interface dynamically.
Let's begin with the XML file that holds our list of available sidebar
topics, named topics.xml; we will use this file to create our initial user
interface:
<topics>
<topic id="topic1"
title="Topic 1"
src="topic1.html"
default="true"/>
<topic id="topic2"
title="Topic 2"
src="topic2.html"/>
<topic id="topic3"
title="Topic 3"
src="topic3.html"/>
</topics>
This is a simple XML file that records each available topic. We assign
each topic several attributes, such as id and title, that we will later
extract using JavaScript to build up the left-hand menu sidebar. Using
an XML file in our application is meant to simulate more sophisticated
AJAX pages that need to cache complex resources.
<html>
<head>
<!-- The X Cross-Browser DHTML Library -->
<script language="JavaScript"
src="../lib/x/x_core.js">
</script>
<script language="JavaScript"
src="../lib/x/x_dom.js">
</script>
<script language="JavaScript"
src="../lib/x/x_event.js">
</script>
We create our basic HTML markup and style it using an open source,
two-column layout, Cascading Style Sheets (CSS) template. We store
the CSS separate from the HTML in advanced.css (view source) to ease
maintenence. The complete advanced.html:
<html>
<head>
<!-- The X Cross-Browser DHTML Library -->
<script language="JavaScript"
src="../lib/x/x_core.js">
</script>
<script language="JavaScript"
src="../lib/x/x_dom.js">
</script>
<script language="JavaScript"
src="../lib/x/x_event.js">
</script>
<body>
<div id="content">
<h1 id="topicTitle">
Topic Title
</h1>
<div id="topicContent">
Topic Content Goes Here
</div>
</div>
<div id="menu">
</div>
</body>
</html>
Much of the bulk of our application is in initialize(). Let's see the full
initialize() method, which we will break down and go over piece by
piece:
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
If this is the first time the page has loaded, we call a method named
loadTopics() to retrieve the list of available topics. loadTopics() uses the
XmlHttpRequest object to remotely fetch the topics.xml file, parse out its
values, and then uses them to create a JavaScript array containing
each topic value. loadTopics() returns an array of available topics,
which we persist into the History Storage API under the hashtable key
"topics". If the user navigates away from this page and then back, the
value will still be available inside the historyStorage class:
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
Let's break out the loadTopics() method. This method first fetches the
remote topics.xml file using the Sarissa utility framework; Sarissa will
automatically handle cross-browser differences in terms of fetching the
file and rendering it into an XML DOM object that we can work with
easily. Next, we parse out the values in our XML file into a JavaScript
array that represents all of the available topics. The full loadTopics()
method:
function loadTopics() {
// load our remote topics.xml document
// synchronously into an XML DOM object
var topicsXML =
Sarissa.getDomDocument();
topicsXML.async = false;
topicsXML.load("topics.xml");
if (currentTopic.isDefault == null ||
currentTopic.isDefault
== undefined)
currentTopic.isDefault = false;
topics.push(currentTopic);
}
return topics;
}
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
function displayTopicsList() {
var menu =
document.getElementById("menu");
for (var i = 0; i < topics.length;
i++) {
// use each topic to update
// our user interface
var newTopic =
document.createElement("a");
newTopic.href = topics[i].src;
newTopic.title = topics[i].title;
// note: avoid using the id attribute of
// hyperlinks if they will clash with a
// location you store into history; if these
// values are the same you can run into
// some strange bugs in Internet Explorer;
// to avoid this, we use setAttribute with
// a custom attribute named "topicID"
newTopic.setAttribute("topicID", topics[i].id);
newTopic.innerHTML = topics[i].title;
menu.appendChild(newTopic);
}
}
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
function displayTopic(topicID) {
var topic;
return content;
}
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
function handleTopicChange(e) {
var evt = new xEvent(e);
var target = evt.target;
var topicID = target.getAttribute("topicID");
function handleTopicChange(e) {
var evt = new xEvent(e);
var target = evt.target;
var topicID = target.getAttribute("topicID");
Next, we extract the topic ID the user wants and display this
topic:
function handleTopicChange(e) {
var evt = new xEvent(e);
var target = evt.target;
var topicID = target.getAttribute("topicID");
We take this topic's content and ID and add it to our DHTML history,
which will automatically update the browser location and persist this
history data:
function handleTopicChange(e) {
var evt = new xEvent(e);
var target = evt.target;
var topicID = target.getAttribute("topicID");
function handleTopicChange(e) {
var evt = new xEvent(e);
var target = evt.target;
var topicID = target.getAttribute("topicID");
function initialize() {
// initialize the DhtmlHistory
// framework
dhtmlHistory.initialize();
function handleHistoryEvent(newLocation,
historyData) {
var topicID = newLocation;
Hi Brad,
Great article! I got it working great in Firefox, and then moved over to
IE6 and am not having any luck. When I add a new location to the
history, I don't seem to get any trigger from the history listener to go
the new location.
I will keep looking into it, but any additional guidance would be greatly
appreciated.
Thanks,
Ron