├── README.md ├── jquery └── jquery.hash.js └── Hash.js /README.md: -------------------------------------------------------------------------------- 1 | # Hash handler 2 | 3 | ## Information 4 | 5 | A JavaScript library that keeps track of the history of changes to the hash 6 | part in the address bar. 7 | 8 | A callback function, "handler", is used to handle changes. The function will 9 | be called once when the library is initialized (usually when the page has 10 | finished loading) and then whenever the hash changes. 11 | 12 | **Warning** for Internet Explorer 7 and below: If an element on the page has 13 | the same ID as the hash used, the history will get messed up. 14 | 15 | Does not support history in Safari 2 and below. 16 | 17 | ## Example 18 | 19 | function handler(newHash, initial) { 20 | if (initial) 21 | alert('Hash is "' + newHash + '"'); 22 | else 23 | alert('Hash changed to "' + newHash + '"'); 24 | } 25 | Hash.init(handler); 26 | Hash.go('abc123'); 27 | 28 | The `initial` argument is a boolean that indicates whether the handler was 29 | called for initial state by `Hash.init` (value will be `true`), or due to 30 | an actual change to the hash (`false`). 31 | 32 | ### jQuery plugin 33 | 34 | Also included is a jQuery plugin that simplifies the use of the Hash library. 35 | 36 | #### Example 37 | 38 | // Add events before calling init to make sure they are triggered for 39 | // initial hash value. 40 | $('div#log').hashchange(function (e, newHash) { 41 | $(this).prepend('

New hash: "' + newHash + '"

'); 42 | }); 43 | // Initialize. 44 | $.hash.init(); 45 | $.hash.go('abc123'); 46 | // Changes hash when the anchor is clicked. Also automatically sets the 47 | // href attribute to "#def456", unless a second argument with a false 48 | // value is supplied. 49 | $('a#my-anchor').hash('def456'); 50 | 51 | ## MIT license 52 | 53 | This project is licensed under an MIT license. 54 | 55 | Copyright (c) 2009-2010 Andreas Blixt 56 | 57 | http://www.opensource.org/licenses/mit-license.php 58 | -------------------------------------------------------------------------------- /jquery/jquery.hash.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2009-2011 Andreas Blixt 3 | * Contributors: Simon Chester 4 | * http://github.com/blixt/js-hash 5 | * MIT License: http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * jQuery hash plugin (Depends on jQuery, Hash) 8 | * Plugin for detecting changes to the hash and for adding history support for 9 | * hashes to certain browsers. 10 | */ 11 | /* Example: 12 | * // Add events before calling init to make sure they are triggered for 13 | * // initial hash value. 14 | * $('div#log').hashchange(function (e, newHash) { 15 | * $(this).prepend('

New hash: "' + newHash + '"

'); 16 | * }); 17 | * // Initialize. 18 | * $.hash.init(); 19 | * $.hash.go('abc123'); 20 | * // Changes hash when the anchor is clicked. Also automatically sets the 21 | * // href attribute to "#def456", unless a second argument with a false 22 | * // value is supplied. 23 | * $('a#my-anchor').hash('def456'); 24 | * 25 | * WARNING for Internet Explorer 7 and below: 26 | * If an element on the page has the same ID as the hash used, the history will 27 | * get messed up. 28 | * 29 | * Does not support history in Safari 2 and below. 30 | * 31 | * 32 | * Updated by Simon Chester (simonches@gmail.com) on 2011-05-16: 33 | * - Updated to reflect Hash.js no longer needing iframe argument 34 | */ 35 | 36 | (function (jQuery, Hash) { 37 | var 38 | // Plugin settings 39 | eventName = 'hashchange', 40 | eventDataName = 'hash.fn', 41 | init, 42 | // Import globals 43 | window = this, 44 | documentMode = document.documentMode, 45 | 46 | // Called whenever the hash changes. 47 | callback = function (newHash) { 48 | jQuery.event.trigger(eventName, [newHash]); 49 | }; 50 | 51 | jQuery.hash = { 52 | init: function () { 53 | // init can only be called once. 54 | if (init) return; 55 | init = 1; 56 | 57 | Hash.init(callback); 58 | }, 59 | 60 | go: Hash.go 61 | }; 62 | 63 | jQuery.fn.hash = function (newHash, changeHref) { 64 | var fn = this.data(eventDataName); 65 | if (fn) this.unbind('click', fn); 66 | 67 | if (typeof newHash == 'string') { 68 | fn = function () { Hash.go(newHash); return false; }; 69 | this.data(eventDataName, fn); 70 | this.click(fn); 71 | if (changeHref || changeHref === undefined) 72 | this.attr('href', '#' + newHash); 73 | } 74 | 75 | return this; 76 | }; 77 | 78 | jQuery.fn[eventName] = function (fn) { 79 | return this.bind(eventName, fn); 80 | }; 81 | })(jQuery, Hash); 82 | -------------------------------------------------------------------------------- /Hash.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2009-2011 Andreas Blixt 3 | * Contributors: Aaron Ogle , 4 | * Matti Virkkunen , 5 | * Simon Chester 6 | * http://github.com/blixt/js-hash 7 | * MIT License: http://www.opensource.org/licenses/mit-license.php 8 | * 9 | * Hash handler 10 | * Keeps track of the history of changes to the hash part in the address bar. 11 | */ 12 | /* WARNING for Internet Explorer 7 and below: 13 | * If an element on the page has the same ID as the hash used, the history will 14 | * get messed up. 15 | * 16 | * Does not support history in Safari 2 and below. 17 | * 18 | * Example: 19 | * function handler(newHash, initial) { 20 | * if (initial) 21 | * alert('Hash is "' + newHash + '"'); 22 | * else 23 | * alert('Hash changed to "' + newHash + '"'); 24 | * } 25 | * Hash.init(handler); 26 | * Hash.go('abc123'); 27 | * 28 | * 29 | * Updated by Simon Chester (simonches@gmail.com) on 2011-05-16: 30 | * - Removed the need for blank.html and the iframe argument by creating the 31 | * iframe on initialization. 32 | * 33 | * Updated by Matti Virkkunen (mvirkkunen@gmail.com) on 2009-11-16: 34 | * - Added second argument to callback that indicates whether the callback is 35 | * due to initial state (true) or due to an actual change to the hash 36 | * (false). 37 | * 38 | * Updated by Aaron Ogle (aogle@avencia.com) on 2009-08-11: 39 | * - Fixed bug where Firefox automatically unescapes location.hash but no 40 | * other browsers do. Always get the hash by parsing location.href and 41 | * never use location.hash. 42 | */ 43 | 44 | var Hash = (function () { 45 | var 46 | // Import globals 47 | window = this, 48 | documentMode = document.documentMode, 49 | history = window.history, 50 | // Plugin variables 51 | callback, hash, 52 | // IE-specific 53 | iframe, 54 | 55 | getHash = function () { 56 | // Internet Explorer 6 (and possibly other browsers) extracts the query 57 | // string out of the location.hash property into the location.search 58 | // property, so we can't rely on it. The location.search property can't be 59 | // relied on either, since if the URL contains a real query string, that's 60 | // what it will be set to. The only way to get the whole hash is to parse 61 | // it from the location.href property. 62 | // 63 | // Another thing to note is that in Internet Explorer 6 and 7 (and possibly 64 | // other browsers), subsequent hashes are removed from the location.href 65 | // (and location.hash) property if the location.search property is set. 66 | // 67 | // Via Aaron: Firefox 3.5 (and below?) always unescape location.hash which 68 | // causes poll to fire the hashchange event twice on escaped hashes. This 69 | // is because the hash variable (escaped) will not match location.hash 70 | // (unescaped.) The only consistent option is to rely completely on 71 | // location.href. 72 | var index = window.location.href.indexOf('#'); 73 | return (index == -1 ? '' : window.location.href.substr(index + 1)); 74 | }, 75 | 76 | // Used by all browsers except Internet Explorer 7 and below. 77 | poll = function () { 78 | var curHash = getHash(); 79 | if (curHash != hash) { 80 | hash = curHash; 81 | callback(curHash, false); 82 | } 83 | }, 84 | 85 | // From: 86 | // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ 87 | isHashChangeSupported = function() { 88 | var eventName = 'onhashchange'; 89 | var isSupported = (eventName in document.body); 90 | if (!isSupported) { 91 | document.body.setAttribute(eventName, 'return;'); 92 | isSupported = typeof document.body[eventName] == 'function'; 93 | } 94 | 95 | // documentMode logic from YUI to filter out IE8 Compat Mode (which 96 | // generates false positives). 97 | return isSupported && (document.documentMode === undefined || 98 | document.documentMode > 7); 99 | }, 100 | 101 | createIframe = function () { 102 | var tempEl = document.createElement(); 103 | tempEl.innerHTML = ''; 105 | var frame = tempEl.childNodes[0]; 106 | document.body.appendChild(frame); 107 | return frame; 108 | }, 109 | 110 | // Used to create a history entry with a value in the iframe. 111 | setIframe = function (newHash) { 112 | try { 113 | var doc = iframe.contentWindow.document; 114 | doc.open(); 115 | doc.write('' + newHash + ''); 116 | doc.close(); 117 | hash = newHash; 118 | } catch (e) { 119 | setTimeout(function () { setIframe(newHash); }, 10); 120 | } 121 | }, 122 | 123 | // Used by Internet Explorer 7 and below to set up an iframe that keeps track 124 | // of history changes. 125 | setUpIframe = function () { 126 | // Don't run until access to the body is allowed. 127 | try { 128 | iframe = createIframe(); 129 | } catch (e) { 130 | setTimeout(setUpIframe, 10); 131 | return; 132 | } 133 | 134 | // Create a history entry for the initial state. 135 | setIframe(hash); 136 | var data = hash; 137 | 138 | setInterval(function () { 139 | var curData, curHash; 140 | 141 | try { 142 | curData = iframe.contentWindow.document.body.innerText; 143 | if (curData != data) { 144 | data = curData; 145 | window.location.hash = hash = curData; 146 | callback(curData, true); 147 | } else { 148 | curHash = getHash(); 149 | if (curHash != hash) setIframe(curHash); 150 | } 151 | } catch (e) { 152 | } 153 | }, 50); 154 | }; 155 | 156 | return { 157 | init: function (cb) { 158 | // init can only be called once. 159 | if (callback) return; 160 | 161 | callback = cb; 162 | 163 | // Keep track of the hash value. 164 | hash = getHash(); 165 | cb(hash, true); 166 | 167 | if (isHashChangeSupported()) { 168 | if (window.addEventListener){ 169 | window.addEventListener('hashchange', poll, false); 170 | } else if (window.attachEvent){ 171 | window.attachEvent('onhashchange', poll); 172 | } 173 | } else { 174 | // Run specific code for Internet Explorer. 175 | if (window.ActiveXObject) { 176 | if (!documentMode || documentMode < 8) { 177 | // Internet Explorer 5.5/6/7 need an iframe for history 178 | // support. 179 | setUpIframe(); 180 | } 181 | } else { 182 | // Change Opera navigation mode to improve history support. 183 | if (history.navigationMode) { 184 | history.navigationMode = 'compatible'; 185 | } 186 | 187 | setInterval(poll, 50); 188 | } 189 | } 190 | }, 191 | 192 | go: function (newHash) { 193 | // Cancel if the new hash is the same as the current one, since there 194 | // is no cross-browser way to keep track of navigation to the exact 195 | // same hash multiple times in a row. A wrapper can handle this by 196 | // adding an incrementing counter to the end of the hash. 197 | if (newHash == hash) return; 198 | if (iframe) { 199 | setIframe(newHash); 200 | } else { 201 | window.location.hash = hash = newHash; 202 | callback(newHash, false); 203 | } 204 | }, 205 | 206 | getHash: getHash 207 | }; 208 | })(); 209 | --------------------------------------------------------------------------------