如何检测位置哈希的更改?

585

我正在使用Ajax和hash实现导航。

有没有一种方法可以检查window.location.hash是否发生了变化,例如:

http://example.com/blah#123 变为 http://example.com/blah#456

如果是在文档加载时检查,它是有效的。

但如果我基于#hash进行导航,在浏览器上按下返回按钮时它不起作用(所以我从blah#456跳转到blah#123)。

地址栏里会显示,但我无法用JavaScript捕获它。


6
看看这个jQuery插件:https://github.com/cowboy/jquery-hashchange - Xavi
9
History.js 支持 HTML5 状态管理功能(所以您不再需要使用哈希值!)并通过哈希更改优雅地退化到 HTML4 浏览器。它支持 jQuery、MooTools 和 Prototype,无需额外的配置即可使用。 - balupton
@balupton,实际上我们仍然需要使用哈希来向用户提供反馈,告诉他一个“新页面”已经插入到他的历史记录中,除非您使用URL更改作为反馈。 - Pacerier
1
[Hasher]https://github.com/millermedeiros/hasher/ - Vishnoo Rath
嗯...我认为你需要更多的jQuery - Penguin9
11个回答

631
唯一真正实现此功能的方法(这也是'reallysimplehistory'使用的方法)是设置一个间隔来不断检查当前哈希值,并将其与之前的哈希值进行比较。我们这样做并让订阅者订阅一个更改事件,如果哈希值发生变化,则触发该事件。虽然不完美,但浏览器确实不支持此事件。
更新以保持答案的新鲜感:
如果您使用jQuery(今天应该是大部分人基本的),则一个好的解决方案是使用jQuery提供的抽象层,通过使用它的事件系统监听窗口对象上的hashchange事件。
$(window).on('hashchange', function() {
  //.. work ..
});

优点在于你可以编写不必考虑hashchange支持的代码,然而你需要使用一些魔法,形式为一个比较不为人知的jQuery功能jQuery特殊事件

使用此功能,您基本上可以为任何事件运行一些设置代码,第一次尝试以任何方式使用该事件时(例如绑定到该事件)。

在此设置代码中,您可以检查本机浏览器支持情况,如果浏览器没有本地实现这个支持,则可以设置单个计时器来轮询更改并触发jQuery事件。

这完全解除了您的代码需要理解此支持问题的束缚,实现此类特殊事件是微不足道的(获取简单的98%工作版本),但为什么要这样做,当别人已经有了呢


29
最新版的Firefox构建(3.6 alpha)现在也支持本地哈希变化事件:https://developer.mozilla.org/en/DOM/window.onhashchange检查此事件确实值得一试,但请注意,当IE8运行在IE7兼容模式下时,它会告诉你该事件存在..不幸的是,该事件不会触发..你需要检查该事件和浏览器似乎不是IE7..叹气(或尝试使用IE的fireEvent方法触发事件)。 - meandmycode
9
目前为止,WebKit会触发hashchange事件,而Safari(稳定版)尚未。 - jholster
54
再补充一则更新消息,hashchange 事件现在得到广泛支持:http://caniuse.com/#search=hash - Paystey
23
我是唯一一个认为未经请求的jQuery答案很烦人的人吗? - Luc
7
此回答已过时。 - user1596138
显示剩余9条评论

308

20
更新:自2011年6月起,FF 5、Safari 5和Chrome 12支持此事件。 - james.garriss
2
这里是hashchange的CanIUse页面。这里是quirksmode上的hashchange页面。IE对大小写敏感性的支持存在缺陷。 - Tobu
3
大家好,不需要在评论区不断添加答案 - 这就是“编辑”按钮的作用。 :) - Michael Martin-Smucker
19
用法:“window.onhashchange = function() { doYourStuff(); }”意思是当浏览器的URL中的哈希标识符发生变化时,执行一个名为“doYourStuff”的函数。 - Chris
4
MDN文档中onhashchange事件的翻译如下:hashchange事件在浏览器地址栏中的URL哈希值发生变化时触发。当用户点击页面上的锚链接或通过JavaScript代码修改URL的哈希值时,该事件将被触发。使用window.onhashchange属性可以为该事件添加事件处理程序函数。通过这种方式,您可以编写适当的JavaScript代码来响应哈希值的更改,并对页面进行相应的操作。然而,请注意,某些旧版浏览器可能不支持此事件。因此,在编写使用该事件的代码时,请确保您的代码具有适当的浏览器兼容性。 - lele1c
这似乎无法与历史记录操作一起使用。有window.onpopstate,但即使如此,它也不总是有效。 - cregox

52
请注意,在Internet Explorer 7和Internet Explorer 9的情况下,if语句将返回true(对于窗口中的"onhashchange"),但是window.onhashchange永远不会触发,因此最好在Internet Explorer的所有版本中存储哈希并每隔100毫秒检查它是否已更改。
    if (("onhashchange" in window) && !($.browser.msie)) {
         window.onhashchange = function () {
              alert(window.location.hash);
         }
         // Or $(window).bind( 'hashchange',function(e) {
         //       alert(window.location.hash);
         //   });
    }
    else {
        var prevHash = window.location.hash;
        window.setInterval(function () {
           if (window.location.hash != prevHash) {
              prevHash = window.location.hash;
              alert(window.location.hash);
           }
        }, 100);
    }

编辑 - 自从jQuery 1.9版本开始,不再支持$.browser.msie。来源:http://api.jquery.com/jquery.browser/


16

我在一个React应用程序中使用这个功能,根据用户所在的视图来显示不同的URL参数。

我使用了哈希参数进行观察。

window.addEventListener('hashchange', doSomethingWithChangeFunction);

那么

function doSomethingWithChangeFunction () {
    let urlParam = window.location.hash; // Get new hash value

    // ... Do something with new hash value
};

它非常有效。它可以与前进和后退浏览器按钮一起使用,也可以在浏览器历史记录中使用。


14

在处理IE浏览器中的历史记录和window.location.hash时,有很多技巧:

  • 正如原问题所述,如果您从a.html#b转到a.html#c,然后点击“后退”按钮,浏览器不知道页面已更改。让我用一个例子来说明:无论您在a.html#b还是a.html#c中,window.location.href都将是'a.html#c'。

  • 实际上,只有当页面中之前存在元素'<a name="#b">'和'<a name="#c">'时,a.html#b和a.html#c才会被存储在历史记录中。

  • 但是,如果您在页面中放置一个iframe,在该iframe中从a.html#b导航到a.html#c,然后点击“后退”按钮,iframe.contentWindow.document.location.href会按预期更改。

  • 如果您在代码中使用'document.domain=something',则无法访问iframe.contentWindow.document.open()(许多历史记录管理器都这样做)。

我知道这不是真正的回答,但也许IE历史记录笔记对某些人有用。


12

自从 Firefox 3.6 版本起,就有了一个名为 window.onhashchange 的事件。请参阅相关文档。


9
你可以很容易地在"window.location"对象的"hash"属性上实现一个观察者("watch"方法)。Firefox有自己的观察对象更改的实现方式,但如果你使用其他实现方式(如JavaScript中观察对象属性变化)-对于其他浏览器,这将起到作用。代码如下:
window.location.watch(
    'hash',
    function(id,oldVal,newVal){
        console.log("the window's hash value has changed from "+oldval+" to "+newVal);
    }
);

然后您可以测试它:

var myHashLink = "home";
window.location = window.location + "#" + myHashLink;

当然,这将触发您的观察者函数。

最好使用 window.location.href 而不是 window.location。 - Codebeat
3
他正在观察window.location.hash,而不是window.location。 - undefined
1
根据文档(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/watch),您必须将 watch 应用于拥有正在更改且您想要观察的属性的对象。 - gion_13
@gion_13 是的,那正是我想要指出的。我所说的“他”指的是你,而且是针对Erwinus的评论。我应该表达得更清楚。感谢你澄清的评论。 - undefined

1
var page_url = 'http://www.yoursite.com/'; // full path leading up to hash;
var current_url_w_hash = page_url + window.location.hash; // now you might have something like: http://www.yoursite.com/#123

function TrackHash() {
    if (document.location != page_url + current_url_w_hash) {
        window.location = document.location;
    }
    return false;
}
var RunTabs = setInterval(TrackHash, 200);

就是这样...现在,每当您点击后退或前进按钮时,页面将根据新的哈希值重新加载。


1

另一个很棒的实现是jQuery History,如果浏览器支持本机onhashchange事件,则会使用它,否则它将适当地使用iframe或间隔来确保成功模拟所有预期功能。它还提供了一个漂亮的接口来绑定到某些状态。

另一个值得注意的项目是jQuery Ajaxy,它基本上是jQuery History的扩展,添加了ajax。因为当您开始在哈希中使用ajax时,它变得相当复杂


1
我一直在使用path.js来进行客户端路由。我发现它非常简洁和轻量级(它也已经发布到了NPM),并且利用基于哈希的导航。

path.js NPM

path.js GitHub


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接