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,它的回调会在页面添加一行红字,然后再点击后退按钮就看到效果了。

onhashchange事件--司徒正美的更多相关文章

  1. [ javascript ] 司徒正美的fadeOut-fadeIn效果!

    首先感谢司徒正美的文章! 在司徒大神的博客看到一个简单的渐入渐出的效果.全然採用js实现. 例如以下: <!doctype html> <html dir="ltr&quo ...

  2. [web前端] 去哪儿网前端架构师司徒正美:如何挑选适合的前端框架?

    原文地址: https://www.jianshu.com/p/6327d4280e3b 最近几年,前端技术迅猛发展,差不多每年都会冒出一款主流的框架. 每次新开业务线或启动新项目时,首先第一件事就是 ...

  3. javascript 45种缓动效果BY司徒正美

    javascript 45种缓动效果 参数 类型 说明 el element 必需,为页面元素 begin number 必需,开始的位置 change number 必需,要移动的距离 durati ...

  4. html标签对应的英文原文 - 司徒正美

    标签 对应英文 说明 <!--> / 注释 <!DOCTYPE> document type 文档类型 <a> anchor 超链接 <abbr> ab ...

  5. onhashchange事件,只需要修改hash值即可响应onhashchange事件中的函数(适用于上一题下一题和跳转页面等功能)

    使用实例: 使用onhashchange事件做一个简单的上一页下一页功能,并且当刷新页面时停留在当前页 html: <!DOCTYPE html><html><body& ...

  6. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  7. JS 事件代理

    事件处理器:onclick.onmouseover.... 在传统的事件处理中,你需要为每一个元素添加或者是删除事件处理器.然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越 ...

  8. javascript事件代理(Event Delegation)

    看了几篇文章,放上来供参考 司徒正美的文章,Event Delegation Made Easy --------------------------------------------------- ...

  9. 了解javascript中的事件(二)

    本文目录如下: 零.寒暄 一.事件的分类 二.事件代理 2.1 问题引出 2.2 什么是事件代理 2.3 完整示例 二.事件代理 三.事件代理思想的用处 四.总结 零.寒暄 这篇博客本该出现在两个月以 ...

随机推荐

  1. How to convert string to wstring?

    How to convert string to wstring? - Codejie's C++ Space - C++博客     How to convert string to wstring ...

  2. ubuntu下tcpdump使用

    Ubuntu默认是安装好了tcpdump工具的,如果没有安装的话使用sudo apt-get install tcpdump即可安装.   (如果遇到tcpdump: no suitable devi ...

  3. c++中使用c语言函数

    在c++中使用c语言的函数时候,该函数必须在c文件中声明extern "C"才可以使用 如:extern "C" c_function_name(int, in ...

  4. How to change a product dropdown attribute to a multiselect in Magento

    First, update the attribute input type to multiselect: UPDATE eav_attribute SET entity_type_id ', at ...

  5. Java线程之二 锁定与等待堵塞原理图

    如上图所看到的.

  6. jsp相对路径和绝对路径小谈

    很长一段时间纠结过JSP中的相对路径和绝对路径,也研究过一段时间,今天趁着有点时间,记下来,也有大家分享一下. 1)我们先来理解一下相对路径 首先还是我们的开始,建一个WEB项目,只是测试一下而已,名 ...

  7. 跨域文件crossdomain.xml在weblogic上的部署

    1.新建一个ROOT文件夹,把crossdomain.xml拷贝到这个文件夹下. 2.在ROOT文件夹下新建一个WEB-INF文件夹 3.在WEB-INF文件夹下建立一个weblogic.xml文件, ...

  8. 学习使用GitHub(一)--之入门

    因为经常Windows和linux系统交替的使用,在实验室一台电脑,在家一台电脑,自己的电脑和实验室的电脑上面的代码往往没法同步,以前由于种种原因(其实就是懒,没有学习GitHub这样的代码管理工具) ...

  9. BZOJ 4016: [FJOI2014]最短路径树问题( 最短路 + 点分治 )

    先跑出最短路的图, 然后对于每个点按照序号从小到大访问孩子, 就可以搞出符合题目的树了. 然后就是经典的点分治做法了. 时间复杂度O(M log N + N log N) -------------- ...

  10. plupload文件上传插件

    一 资源文档 二 基本使用 三 可能遇到的问题 一 资源文档 Git仓库地址:https://github.com/moxiecode/plupload 一个中文速查:http://www.cnblo ...