Making Friends
with the Browser

Ajax, Back Buttons and Bookmarks

Brian Dillard, Ajax Lead
Pathfinder Development, Chicago

Wednesday 01 October 2008
The Ajax Experience 2008 Boston

Slides: http://labs.pathf.com/ajax/tae2008boston/

Who am I?

Who are you?

Developers and user experience folks who ...

  1. want to meet user expectations.
  2. want to understand the Ajax/back button problem.
  3. aren't afraid of a little code.

How we'll spend the next 90 minutes

Watch
  • A brief history of Ajax history management
  • Current approaches to Ajax history
  • Lessons from Really Simple History
  • The future of Ajax history management

1. A brief history of Ajax history management

Defining a problem

(the infamous broken back button)

Breaking your back buttons is like saying, "don't click this button more than once."

You're asking users to change 15 years of ingrained expectations.

Remember the infinite meta-refresh loop? It's like that, too.

Defining another problem

(bookmarks that go nowhere)

Breaking bookmarks is like saying, "get lost ... literally."

You're 86ing your SEO and letting users know they can't rely on your site.

So who needs Ajax history management, anyway?

Travis

You looking at me?

Ajax history management: A problem older than Ajax itself

This partial loss of browser History is not optimal, but it is offset by PPR's benefits. PPR not only improves application performance, but it also improves user experience of the application -- unlike full page reloads, PPR does not does not "flash" and force the user to the top of the page, and thus provides a minimum of distraction from the task at hand.

- PPR (Partial Page Rendering), Oracle, 2003

Q: How did we break the history stack and bookmark mechanism so badly?

A: New technologies, plus a lack of standards.

About the "standard" history stack

  • HTML 4.01: No explicit history spec.
  • DOM Level 0 provides a window.history object.
  • The window.history interface defines only bare-bones properties and methods
    • back, forward and go methods
    • length property
  • Browser vendors all follow the same model for GUI access... mostly.
    • back and forward buttons
    • history menu

How Ajax breaks window.history

  • If the URL doesn't change, then no history entry gets created.
  • If the URL doesn't include information about an Ajax application's state, then the application can't recreate that state.

Q: So how do we fix it?

A: For now, we hack

Ajax history managers work by manipulating browser quirks to create new entries in the history stack.

Tricking the browser

  • HTML 4.01 provides the fragment identifier, a.k.a the hash.
  • In some browsers, changes to the current URL's fragment identifier create a history entry without hitting the server.
  • In other browsers, navigation within an iframe creates history entries even if the parent document's URL doesn't change.
  • In most browsers, unsubmitted forms retain their values across the current session.

Steps in the history management process

  1. Trick the browser into creating a new entry in the history stack using the hash or an iframe.
  2. Stash data in that history entry in the hash token or in the iframe title.
  3. Create a timer function that waits for the hash/iframe to change as the result of user action.
  4. Trigger a callback that grabs your stashed data from the hash/iframe and passes it back to your application.
  5. Let your Ajax application do the rest.

Sounds simple!

So why is Ajax history management so complicated?

IE6/7
iframe manipulation
Gecko
hash changes
Opera 9
broken timers
Safari 2
broken history.length
Safari 3.0/Win
just broken
IE8
onhashchange
Chrome
broken form fields

2. Current approaches to Ajax history management

Many major webapps now employ custom history management systems

Popular libraries also have their own history systems

They're all solid, but we're going to look at single-purpose libraries instead.

Why choose a particular history manager?

As with general-purpose JavaScript frameworks, it's all a matter of API

The right tool for the right team, the right technology & the right project

Really Simple History Overview

  • Brad Neuberg of Dojo (and now Google) created it.
  • I currently maintain it.
  • I'm looking to share that responsbility.
  • More on that later.

Really Simple History Features

  • Isn't tied to any specific low-level Ajax framework.
  • Uses hidden form fields to retain data across a session
  • Lets you take detours and still retain history.
  • Stores state as key/value pairs.
  • Keys live in the browser hash or an iframe.
  • Values are JSON data stashed in a hidden form field.
  • Keys last across sessions; values last only within a session.

Really Simple History Timeline

Version 0.4

  • 2005
  • Brad's last version
  • supported IE6, FF1.5

Version 0.6

  • 2007
  • my first version
  • supports IE7, Safari 2, Opera 9

Version 0.8

  • 2008
  • in progress
  • more on that later

RSH API

  • Two objects: dhtmlHistory and historyStorage
  • dhtmlHistory has most of the public methods
  • create uses document.write to create its inner workings
  • init grabs the initial state and sets things up
  • addListener binds history changes to a callback function
  • add creates a new entry in the history stack

Show and tell (just view source)

dsHistory

  • Developed by Andrew Mattie
  • Uses iframes even in non-IE browsers so history points don't require hash changes
  • Hash changes for bookmarks
  • Binds functions rather than data to each history point
  • No Safari 2 or Opera support

Show and tell

JavaScript State Manager (JSSM)

  • Developed by Nathan Hammond
  • Very new
  • Rewrite and optimization of Really Simple History
  • Tied to jQuery ... for now
  • Supports most common use cases only ... for now
  • Adds some new form-related functionality

Show and tell

3. What I learned from Really Simple History

Lessons from RSH 0.6

  • Don't couple tightly to current browser limitations
  • Avoid the big-ball-of-wax approach
  • Try to be future-proof
  • Cover the common cases
  • Leave the bells and whistles for plugins
  • Build hooks for community participation

Goals for RSH 0.8

  • Support Chrome, IE8, Safari 3, Opera 9.5
  • Provide defensive browser-sniffing; assume less
  • Isolate each UA-specific implementation
  • Move secondary features, such as storage, to plugins
  • Provide plenty of callback hooks
  • Borrow optimizations from other libraries
  • Make collaboration easier

Show and tell

Goals for RSH 1.0

  • Circle back with JSSM
  • Share code stewardship with Nathan Hammond
  • Pull UA-specific code dynamically
  • Provide full unit testing suite
  • Write thorough documentation
  • Enable customizable builds
  • Foster an add-on ecosystem
  • Build full-on, real-world example apps

4. The future of Ajax history management

Alternate approaches

Zach Leatherman's CSS-only breakthrough

Additional pain points

  • The document title problem
  • IE6/7 problems: multiple iframes; manual URL changes; peekaboo history entries; same domain issue
  • The TinyMCE problem
  • Code brittleness

HTML 5 history specification

The good news:

  • IE8 supports onhashchanged
  • No more iframes!
  • No more timers!

Yay ... right?

The bad news:

  • Still no history.pushState
  • Still no history.clearState
  • No way to handle titles gracefully (demo)

Q: How long will there still be a need for history management libraries?

A: It all depends on HTML 5's final specification and adoption speed

In other words, for a very long time.

  • Questions?

  • Comments?

  • Clarifications?

Most photos courtesy of Flickr Creative Commons photographers; click images for credits