onhashchange事件是針對AJAX無縫刷新導致后退鍵失效而產生的事件,因此屬於一個夠新的事件,瀏覽器兼容性如下:
Feature | Chrome | Firefox | IE | Opera | Safari |
---|---|---|---|---|---|
support | 5.0 | 3.6 (1.9.2) | 8.0 | 10.6 | 5.0 |
由於chrome引發的版本號競賽,現在chrome20+,firefox16+,opera12了,因此對於標准瀏覽器我們不必顧慮支持問題,精力集中在IE678上。IE8在兼容模式下雖然有此事件,但不生效。這個檢測也很簡單。至於如何產生歷史,這也很簡單,直接在隱藏iframe中調用document.write方法就行。hash的變化,是通過定時器檢測,不十分及時,但對於堅持IE67的操蛋用戶就不應該給好臉色他們看!
如何觀察hash的變化呢?這其實有三個hash值,一個是主窗口之前的hash值(last_hash),主窗口當前的hash值,一個是iframe中的hash值(history_hash),我們可以比較前兩者得知hashchange,但當用戶點擊后退按鈕后,AJAX引發的效果是作用於iframe中的,因此這時是比較last_hash與history_hash。發生變化后,我們再手動修改主窗口的hash,觸發onhashchange回調。
最后提一提hash值的提取,這里存在兩個兼容性問題:
IE6直接用location.hash取hash,可能會取少一部分內容:
比如 http://www.cnblogs.com/rubylouvre#stream/xxxxx?lang=zh_c
ie6 => location.hash = #stream/xxxxx
其他瀏覽器 => location.hash = #stream/xxxxx?lang=zh_c
firefox 會自作多情對hash進行decodeURIComponent
比如 http://www.cnblogs.com/rubylouvre/#!/home/q={%22thedate%22:%2220121010~20121010%22}
firefox 15 => #!/home/q={"thedate":"20121010~20121010"}
其他瀏覽器 => #!/home/q={%22thedate%22:%2220121010~20121010%22}
下面是mass Framework中的實現
define("hashchange", ["$event"], function(){ $.log("已加載hashchange模塊 by 司徒正美") var hashchange = 'hashchange', DOC = document, documentMode = DOC.documentMode, supportHashChange = ('on' + hashchange in window) && ( documentMode === void 0 || documentMode > 7 ); $.fn[ hashchange ] = function(callback){ return callback? this.bind(hashchange, callback ) : this.fire( hashchange); } $.fn[ hashchange ].delay = 50; if(!supportHashChange){ $.log("不支持hashchange,使用iframe加定時器模擬") var iframe, timeoutID, html = '<!doctype html><html><body>#{0}</body></html>' if( $.fn[ hashchange ].domain){ html = html.replace("<body>","<script>document.domain ="+ $.fn[ hashchange ].domain +"</script><body>" ) } function getHash ( url) {//用於取得當前窗口或iframe窗口的hash值 url = url || DOC.URL return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); } function getHistory(){ return getHash(iframe.location); } function setHistory(hash, history_hash){ var doc = iframe.document; if ( hash !== history_hash ) {//只有當新hash不等於iframe中的hash才重寫 //用於產生歷史 doc.open(); doc.write($.format(html, hash)); doc.close(); } } var last_hash = getHash(), history_hash, hash = "#"; $.eventAdapter[ hashchange ] = { setup: function(desc) { $.require("ready", function(){ if (!iframe) { //創建一個隱藏的iframe,使用這博文提供的技術 http://www.paciellogroup.com/blog/?p=604. //iframe是直接加載父頁面,為了防止死循環,在DOM樹未建完之前就擦入新的內容 var el = $('<iframe tabindex="-1" style="display:none" widht=0 height=0 title="empty" />').appendTo( document.body )[0], fn iframe = el.contentWindow $.bind(el, "load",fn = function(){ $.unbind(el, "load", fn) var doc = iframe.document doc.open(); doc.write($.format(html, hash)) doc.close(); timeoutID = setInterval(poll, $.fn[ hashchange ].delay) }); function poll() { var hash = getHash(),//取得主窗口中的hash history_hash = iframe.document.body.innerText;//取得現在iframe中的hash if(hash !== last_hash){//如果是主窗口的hash發生變化 setHistory(last_hash = hash, history_hash ) $(desc.currentTarget).fire(hashchange) }else if(history_hash !== last_hash){//如果按下回退鍵, location.href = location.href.replace( /#.*/, '' ) + history_hash; } } } }); }, teardown: function(){ if(!iframe){ clearTimeout(timeoutID); $(iframe).remove(); iframe = 0; } } }; } })
具體例子可見這里
打開后點擊“運行代碼”,然后點擊頁面觸發hashchange,它的回調會在頁面添加一行紅字,然后再點擊后退按鈕就看到效果了。