在 HTML5 之前,为了保护客户端浏览信息的安全和隐私,history 对象禁止 JavaScript 脚本直接操作这些访问信息。不过 HTML5 新增了一个 History API,该 API 允许用户通过 JavaScript 管理浏览器的历史记录,实现无刷新更改浏览器地址栏的地址,配合 History + Ajax 可以设计不需要刷新页面的跳转。
操作历史记录
在历史记录中后退
window.history.back();1
这行代码等效于在浏览器的工具栏上单击“返回”按钮。
在历史记录中前进
window.history.forward();1
这行代码等效于浏览器中单击“前进”按钮。
移动到指定的历史记录点
使用 go() 方法从当前会话的历史记录中加载页面。当前页面位置索引值为 0,上一页就是 -1,下一页为 1,以此类推。
window.history.go(-1); //相当于调用 back() window.history.go(1); //相当于调用forward()
length 属性
使用 length 属性可以了解历史记录栈中一共有多少页。
var num = window.history.length;
添加和修改历史记录条目
HTML5 新增 history.pushState() 和 history.replaceState() 方法,允许用户逐条添加和修改历史记录条目。
使用 history.pushState() 方法可以改变 referrer 的值,而在调用该方法后创建的 XMLHttpRequest 对象会在 HTTP 请求头中使用这个值。referrer 的值则是创建 XMLHttpRequest 对象时所处的窗口的 URL。
【示例】假设 http://123.com/foo.html 页面将执行下面 JavaScript 代码。
var stateObj = {foo : "bar"}; history.pushState (stateObj, "page 2", "bar.html");
这时浏览器的地址栏将显示 http://123.com/bar.html,但不会加载 bar.html 页面,也不会检查 bar.html 是否存在。
如果现在导航到 http://123.com/ 页面,然后单击“后退”按钮,此时地址栏会显示 http://123.com/bar.html,并且会触发 popstate 事件,该事件中的状态对象会包含 stateObj 的一个拷贝。
如果再次单击“后退”按钮,URL 将返回 http://123.com/foo.html,文档将触发另一个 popstate 事件,这次的状态对象为 null,回退同样不会改变文档内容。
pushState() 方法
pushState() 方法包含 3 个参数,简单说明如下:
- 状态对象。状态对象是一个 JavaScript 对象直接量,与调用 pushState() 方法创建的新历史记录条目相关联。无论何时用户导航到新创建的状态,popstate 事件都会被触发,并且事件对象的 state 属性都包含历史记录条目的状态对象的拷贝。
- 标题。可以传入一个简短的标题,标明将要进入的状态。FireFox 浏览器目前忽略该参数,考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。
- 可选参数。新的历史记录条目的地址。
浏览器不会在调用 pushState() 方法后加载该地址,如果不指定则为文档当前 URL。
调用 pushState() 方法类似于设置 window.location='#foo',它们都会在当前文档内创建和激活新的历史记录条目。但 pushState() 有自己的优势。
- 新的 URL 可以是任意的同源 URL。相反,使用 window.location 方法时,只有仅修改 hash 才能保证停留在相同的 document 中。
- 根据个人需要决定是否修改 URL。相反,设置 window.location='#foo',只有在当前 hash 值不是 foo 时才创建一条新的历史记录。
- 可以在新的历史记录条目中添加抽象数据。如果使用基于 hash 的方法,只能把相关数据转码成一个很短的字符串。
pushState() 方法永远不会触发 hashchange 事件。
replaceState() 方法
history.replaceState() 与 history.pushState() 用法相同,都包含 3 个相同的参数。不同之处是:pushState() 是在 history 栈中添加一个新的条目,replaceState() 是替换当前的记录值。例如,history 栈中有两个栈块,一个标记为 1,另一个标记为 2,现在有第 3 个栈块,标记为 3。当执行 pushState() 时,栈块 3 将被添加栈中,栈就有 3 个栈块了;而当执行 replaceState() 时,将使用栈块 3 替换当前激活的栈块 2,history 的记录条数不变。也就是说,pushState() 会让 history 的数量加 1。
为了响应用户的某些操作,需要更新当前历史记录条目的状态对象或 URL 时,使用 replaceState() 方法会特别合适。
popstate 事件
每当激活的历史记录发生变化时,都会触发 popstate 事件。如果被激活的历史记录条目是由 pushState() 创建,或者是被 replaceState() 方法替换的,popstate 事件的状态属性将包含历史记录的状态对象的一个拷贝。
当浏览会话历史记录时,不管是单击浏览器工具栏中的“前进”或者“后退”按钮,还是使用 JavaScript 的 history.go() 和 history.back() 方法,popstate 事件都会被触发。
读取历史状态
在页面加载时,可能会包含一个非空的状态对象。这种情况是会发生的,例如,如果页面中使用 pushState() 或 replaceState() 方法设置了一个状态对象,然后重启浏览器。当页面重新加载时,页面会触发 onload 事件,但不会触发 popstate 事件。但是,如果读取 history.state 属性,会得到一个与 popstate 事件触发时一样的状态对象。
可以直接读取当前历史记录条目的状态,而不需要等待 popstate 事件。
var currentState = history.state;
案例:设计无刷新导航
本例设计一个无刷新页面导航,在首页(index.html)包含一个导航列表,当用户单击不同的列表项目时,首页(index.html)的内容容器(<div id="content">)会自动更新内容,正确显示对应目标页面的 HTML 内容,同时浏览器地址栏正确显示目标页面的 URL(但是首页并没有被刷新),而不是仅显示目标页面。显示效果如图所示。
在浏览器工具栏中单击“后退”按钮,浏览器能够正确显示上一次单击的链接地址,虽然页面并没有被刷新,同时地址栏中正确显示上一次浏览页面的 URL,如下图所示。如果没有 History API 支持,使用 Ajax 实现异步请求时,工具栏中的“后退”按钮的是无效的。
但是,如果在工具栏中单击“刷新”按钮,则页面将根据地址栏的 URL 信息重新刷新页面,显示独立的目标页面,效果如下图所示。
此时,如果再单击工具栏中的“后退”和“前进”按钮,会发现导航功能失效。页面总是显示目标页面,如下图所示。这说明使用 History API 控制导航与浏览器导航功能存在差异,一个是 JavaScript 脚本控制,一个是系统自动控制。
【操作步骤】
1) 设计首页(index.html)。新建文档,保存为 index.html,构建 HTML 导航结构。
<h1>History API 示例</h1> <ul id="menu"> <li><a href="news.html">News</a></li> <li><a href="about.html">About</a></li> <li><a href="contact.html">Contact</a></li> </ul> <div id="content"> <h2>当前页内容:index.html</h2> </div>
2) 本例使用 jQuery 作为辅助操作,因此在文档头部位置导入 jQuery 框架。
<script src="jquery/jquery-1.11.0.js" type="text/javascript"></script>
3) 定义异步请求函数。该函数根据参数 url 值,异步加载目标地址的页面内容,把它置入内容容器(<div id="content">)中,并根据第 2 个参数 addEntry 的值执行额外操作。如果第 2 个参数值为 true,则使用 history.pushState() 方法把目标地址推入浏览器历史记录堆栈中。
function getContent (url, addEntry) { $.get (url).done(function (data) { //异步请求 $('#content').html (data); //动态加载目标页面 if (addEntry == true) { history.pushState(none, none, url); //把目标地址推入浏览器历史记录堆栈中 } }); }
4) 在页面初始化事件处理函数中,为每个导航链接绑定 click 事件,在 click 事件处理函数中调用 getContent() 函数,同时阻止页面的刷新操作。
$ (function () { $ ('#menu a').on ('onclick', function (e) { e.preventDefault (); //阻止页面刷新操作 var href = $ (this).attr('href'); getContent (href, true); //执行页面内容更新操作 $ ('#menu a').removeClass ('active'); $ (this).addClass ('active'); }); });
5) 注册 popstate 事件,跟踪浏览器历史记录的变化,如果发生变化,则调用 getContent() 函数更新页面内容,但是不再把目标地址添加到历史记录堆栈中。
window.addEventListener ("popstate", function (e) { getContent (location.pathname,false); });
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/23210.html