History & Back Button Support using jQuery

History & Back Button Support using jQuery Featuring Ben Alman's (@cowboy) jQuery BBQ Plugin

by bizamajig

HTML

<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.0/themes/humanity/jquery-ui.css">
<script src="http://github.com/cowboy/jquery-bbq/raw/v1.2.1/jquery.ba-bbq.js"></script>
<script src="http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js"></script>
<script src="http://github.com/jquery/jquery-metadata/raw/master/jquery.metadata.js"></script>
<link rel="stylesheet" href="http://yui.yahooapis.com/3.1.0/build/cssreset/reset-min.css">
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<h1>History &amp; Back Button Support with <a href="http://benalman.com/projects/jquery-bbq-plugin/">jQuery BBQ Plugin</a></h1>

<ul id="directions">
    <li>Double-Click an item to retrieve recent tweets</li>
    <li>Reorder the items by dragging them up or down</li>
    <li>After interacting a while, try clicking the Back Button several times</li>
</ul>

<div class="demo">
    <ul id="sortable">
        <li id="1" class="ui-state-default { userName : 'jquery', count : '1' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>jQuery</li>
        <li id="2" class="ui-state-default { userName : 'jeresig', count : '2' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>John Resig</li>
        <li id="3" class="ui-state-default { userName : 'paul_irish', count : '3' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Paul Irish</li>
        <li id="4" class="ui-state-default { userName : 'cowboy', count : '2' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Ben Alman</li>
        <li id="5" class="ui-state-default { userName : 'reybango', count : '1' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Rey Bango</li>
        <li id="6" class="ui-state-default { userName : 'codylindley', count : '2' }"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Cody Lindley</li>
        <li id="7" class="ui-state-default { userName : 'elijahmanor', count : '3' }"><span class="ui-icon...

CSS

article, aside, figure, footer, header, hgroup, menu, nav, section { display: block; }
#sortable { list-style-type: none; margin: 0; padding: 0; width: 60%; }
#sortable li { margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 18px; cursor: pointer; }
#sortable li span { position: absolute; margin-left: -1.3em; }
#dialog ol { margin-left: 20px; list-style-type: decimal; }
ul#directions { list-style-type: disc; margin-left: 20px; margin-bottom: 15px; }
#demo { padding-top: 15px; }
body { font-family: Arial, Helvetica, sans-serif; font-size: 1em; }
h1, h2 { font-weight: 700; font-size: 3em; }
h1, a { color: #000000; }
a:hover { color: #0000FF; }
h1 { font-size: 120%; margin-bottom: 0.6em; }
#show {position: fixed; top: 0px; right: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(238, 238, 238); color: rgb(33, 33, 33); border-bottom-left-radius: 10px 10px; border-bottom-width: 1px; border-left-width: 1px; border-bottom-style: solid; border-left-style: solid; border-bottom-color: rgb(153, 153, 153); border-left-color: rgb(153, 153, 153); border-right-width: 0px; border-right-style: initial; border-right-color: initial; border-top-width: 0px; border-top-style: initial; border-top-color: initial; text-decoration: none; font: normal normal normal 14px/normal Helvetica, Arial; display: block; background-position: initial initial; background-repeat: initial initial; }

JavaScript

//Setup the sortable list and on update save the sequence array in cache
var sortable = $("#sortable").sortable({
    delay: 500,
    update: function(event, ui) {
        //Generate unique id & store list item ids in cache
        var indexes = $(this).sortable("toArray");
        var sequenceId = new Date().valueOf().toString();
        $(this).data('sequence').cache[sequenceId] = indexes;
        $.bbq.pushState({ sequenceId: sequenceId });
    }
}).disableSelection();

//When a list item is clicked push that dialog id to BBQ
$("#sortable li").live('dblclick', function(e) {
    $.bbq.pushState({ dialogId : $(this).attr('id')});
    return false;
});

//Set initial sequence state of page on 1st page load
var initialSequence = sortable.sortable("toArray");
sortable.data('sequence', {
    cache: {
        '': initialSequence
    }
});

//Setup the dialog and when closed remove the dialog id from BBQ
var dialog = $('#dialog').dialog({
    autoOpen: false,
    width: 400,
    modal: true,
    buttons: {
        "Close": function() {
            $(this).dialog("close");
        }
    },
    close : function(event, ui) {
        $.bbq.removeState("dialogId");
    }
});

//Open dialog and sort list items if BBQ contains state for them
$(window).bind("hashchange", function(e) {
        //sequenceId: Key in cache that refers to list item sequence indexes
    var sequenceId = e.getState("sequenceId") || '',
        //dialogId: Id of the list item that should be used in open dialog
        dialogId = e.getState("dialogId") || '',
        //metadata: Metadata attached to the list item (userName & count)
        metadata = dialogId ? $('#' + dialogId).metadata() : null,
        //indexes: Sequence array of the list item ids
        indexes = sortable.data('sequence').cache[sequenceId];
    
    //Open dialog using id of list item or close if not present
    if(dialogId) {
        appendTweets(metadata.userName, metadata.count, $("#output")); 
        dialog.dialog('open');
...