jquery.ba-hashchange-2.0.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*!
  2. * jQuery hashchange event - v2.0 - 4/18/2020
  3. * http://benalman.com/projects/jquery-hashchange-plugin/
  4. *
  5. * Copyright (c) 2010 "Cowboy" Ben Alman
  6. * Dual licensed under the MIT and GPL licenses.
  7. * http://benalman.com/about/license/
  8. */
  9. // Script: jQuery hashchange event
  10. //
  11. // *Version: 2.0, Last updated: 4/18/2020*
  12. //
  13. // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
  14. // GitHub - http://github.com/cowboy/jquery-hashchange/
  15. // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
  16. // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
  17. //
  18. // About: License
  19. //
  20. // Copyright (c) 2010 "Cowboy" Ben Alman,
  21. // Dual licensed under the MIT and GPL licenses.
  22. // http://benalman.com/about/license/
  23. //
  24. // About: Examples
  25. //
  26. // These working examples, complete with fully commented code, illustrate a few
  27. // ways in which this plugin can be used.
  28. //
  29. // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
  30. // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
  31. //
  32. // About: Support and Testing
  33. //
  34. // Information about what version or versions of jQuery this plugin has been
  35. // tested with, what browsers it has been tested in, and where the unit tests
  36. // reside (so you can test it yourself).
  37. //
  38. // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
  39. // Browsers Tested - Firefox 2-4, Chrome 5-6, Safari 3.2-5,
  40. // Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
  41. // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
  42. //
  43. // About: Known issues
  44. //
  45. // While this jQuery hashchange event implementation is quite stable and
  46. // robust, there are a few unfortunate browser bugs surrounding expected
  47. // hashchange event-based behaviors, independent of any JavaScript
  48. // window.onhashchange abstraction. See the following examples for more
  49. // information:
  50. //
  51. // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
  52. // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
  53. // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
  54. // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
  55. //
  56. // Also note that should a browser natively support the window.onhashchange
  57. // event, but not report that it does, the fallback polling loop will be used.
  58. //
  59. (function ($, window, undefined) {
  60. '$:nomunge'; // Used by YUI compressor.
  61. // Reused string.
  62. var str_hashchange = 'hashchange',
  63. // Method / object references.
  64. doc = document,
  65. fake_onhashchange,
  66. special = $.event.special,
  67. // Does the browser support window.onhashchange? Note that IE8 running in
  68. // IE7 compatibility mode reports true for 'onhashchange' in window, even
  69. // though the event isn't supported, so also test document.documentMode.
  70. doc_mode = doc.documentMode,
  71. supports_onhashchange = 'on' + str_hashchange in window && (doc_mode === undefined || doc_mode > 7);
  72. // Get location.hash (or what you'd expect location.hash to be) sans any
  73. // leading #. Thanks for making this necessary, Firefox!
  74. function get_fragment(url) {
  75. url = url || location.href;
  76. return '#' + url.replace(/^[^#]*#?(.*)$/, '$1');
  77. };
  78. // Method: jQuery.fn.hashchange
  79. //
  80. // Bind a handler to the window.onhashchange event or trigger all bound
  81. // window.onhashchange event handlers. This behavior is consistent with
  82. // jQuery's built-in event handlers.
  83. //
  84. // Usage:
  85. //
  86. // > jQuery(window).hashchange( [ handler ] );
  87. //
  88. // Arguments:
  89. //
  90. // handler - (Function) Optional handler to be bound to the hashchange
  91. // event. This is a "shortcut" for the more verbose form:
  92. // jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
  93. // all bound window.onhashchange event handlers will be triggered. This
  94. // is a shortcut for the more verbose
  95. // jQuery(window).trigger( 'hashchange' ). These forms are described in
  96. // the <hashchange event> section.
  97. //
  98. // Returns:
  99. //
  100. // (jQuery) The initial jQuery collection of elements.
  101. // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
  102. // $(elem).hashchange() for triggering, like jQuery does for built-in events.
  103. $.fn[str_hashchange] = function (fn) {
  104. return fn ? this.bind(str_hashchange, fn) : this.trigger(str_hashchange);
  105. };
  106. // Property: jQuery.fn.hashchange.delay
  107. //
  108. // The numeric interval (in milliseconds) at which the <hashchange event>
  109. // polling loop executes. Defaults to 50.
  110. // Property: jQuery.fn.hashchange.domain
  111. //
  112. // If you're setting document.domain in your JavaScript, and you want hash
  113. // history to work in IE6/7, not only must this property be set, but you must
  114. // also set document.domain BEFORE jQuery is loaded into the page. This
  115. // property is only applicable if you are supporting IE6/7 (or IE8 operating
  116. // in "IE7 compatibility" mode).
  117. //
  118. // In addition, the <jQuery.fn.hashchange.src> property must be set to the
  119. // path of the included "document-domain.html" file, which can be renamed or
  120. // modified if necessary (note that the document.domain specified must be the
  121. // same in both your main JavaScript as well as in this file).
  122. //
  123. // Usage:
  124. //
  125. // jQuery.fn.hashchange.domain = document.domain;
  126. // Property: jQuery.fn.hashchange.src
  127. //
  128. // If, for some reason, you need to specify an Iframe src file (for example,
  129. // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
  130. // do so using this property. Note that when using this property, history
  131. // won't be recorded in IE6/7 until the Iframe src file loads. This property
  132. // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
  133. // compatibility" mode).
  134. //
  135. // Usage:
  136. //
  137. // jQuery.fn.hashchange.src = 'path/to/file.html';
  138. $.fn[str_hashchange].delay = 50;
  139. /*
  140. $.fn[ str_hashchange ].domain = null;
  141. $.fn[ str_hashchange ].src = null;
  142. */
  143. // Event: hashchange event
  144. //
  145. // Fired when location.hash changes. In browsers that support it, the native
  146. // HTML5 window.onhashchange event is used, otherwise a polling loop is
  147. // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
  148. // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
  149. // compatibility" mode), a hidden Iframe is created to allow the back button
  150. // and hash-based history to work.
  151. //
  152. // Usage as described in <jQuery.fn.hashchange>:
  153. //
  154. // > // Bind an event handler.
  155. // > jQuery(window).hashchange( function(e) {
  156. // > var hash = location.hash;
  157. // > ...
  158. // > });
  159. // >
  160. // > // Manually trigger the event handler.
  161. // > jQuery(window).hashchange();
  162. //
  163. // A more verbose usage that allows for event namespacing:
  164. //
  165. // > // Bind an event handler.
  166. // > jQuery(window).bind( 'hashchange', function(e) {
  167. // > var hash = location.hash;
  168. // > ...
  169. // > });
  170. // >
  171. // > // Manually trigger the event handler.
  172. // > jQuery(window).trigger( 'hashchange' );
  173. //
  174. // Additional Notes:
  175. //
  176. // * The polling loop and Iframe are not created until at least one handler
  177. // is actually bound to the 'hashchange' event.
  178. // * If you need the bound handler(s) to execute immediately, in cases where
  179. // a location.hash exists on page load, via bookmark or page refresh for
  180. // example, use jQuery(window).hashchange() or the more verbose
  181. // jQuery(window).trigger( 'hashchange' ).
  182. // * The event can be bound before DOM ready, but since it won't be usable
  183. // before then in IE6/7 (due to the necessary Iframe), recommended usage is
  184. // to bind it inside a DOM ready handler.
  185. // Override existing $.event.special.hashchange methods (allowing this plugin
  186. // to be defined after jQuery BBQ in BBQ's source code).
  187. special[str_hashchange] = $.extend(special[str_hashchange], {
  188. // Called only when the first 'hashchange' event is bound to window.
  189. setup: function () {
  190. // If window.onhashchange is supported natively, there's nothing to do..
  191. if (supports_onhashchange) { return false; }
  192. // Otherwise, we need to create our own. And we don't want to call this
  193. // until the user binds to the event, just in case they never do, since it
  194. // will create a polling loop and possibly even a hidden Iframe.
  195. $(fake_onhashchange.start);
  196. },
  197. // Called only when the last 'hashchange' event is unbound from window.
  198. teardown: function () {
  199. // If window.onhashchange is supported natively, there's nothing to do..
  200. if (supports_onhashchange) { return false; }
  201. // Otherwise, we need to stop ours (if possible).
  202. $(fake_onhashchange.stop);
  203. }
  204. });
  205. // fake_onhashchange does all the work of triggering the window.onhashchange
  206. // event for browsers that don't natively support it, including creating a
  207. // polling loop to watch for hash changes and in IE 6/7 creating a hidden
  208. // Iframe to enable back and forward.
  209. fake_onhashchange = (function () {
  210. var self = {},
  211. timeout_id,
  212. // Remember the initial hash so it doesn't get triggered immediately.
  213. last_hash = get_fragment(),
  214. fn_retval = function (val) { return val; },
  215. history_set = fn_retval,
  216. history_get = fn_retval;
  217. // Start the polling loop.
  218. self.start = function () {
  219. timeout_id || poll();
  220. };
  221. // Stop the polling loop.
  222. self.stop = function () {
  223. timeout_id && clearTimeout(timeout_id);
  224. timeout_id = undefined;
  225. };
  226. // This polling loop checks every $.fn.hashchange.delay milliseconds to see
  227. // if location.hash has changed, and triggers the 'hashchange' event on
  228. // window when necessary.
  229. function poll() {
  230. var hash = get_fragment(),
  231. history_hash = history_get(last_hash);
  232. if (hash !== last_hash) {
  233. history_set(last_hash = hash, history_hash);
  234. $(window).trigger(str_hashchange);
  235. } else if (history_hash !== last_hash) {
  236. location.href = location.href.replace(/#.*/, '') + history_hash;
  237. }
  238. timeout_id = setTimeout(poll, $.fn[str_hashchange].delay);
  239. };
  240. return self;
  241. })();
  242. })(jQuery, this);