- 简介
- 历史
- 历史(续)
- 历史(续二)
- 历史(续三)
- 检测 HTML5 特性
- 检测 HTML5 特性(续)
- 检测 HTML5 特性(续二)
- 检测 HTML5 特性(续三)
- 它的含义是什么?
- 它的含义是什么?(续)
- 它的含义是什么?(续二)
- 它的含义是什么?(续三)
- 它的含义是什么?(续四)
- 它的含义是什么?(续五)
- 绘图
- 绘图(续)
- 绘图(续二)
- 绘图(续三)
- 绘图(续四)
- 绘图(续五)
- Web 视频
- Web 视频(续)
- Web 视频(续二)
- Web 视频(续三)
- Web 视频(续四)
- 地理位置
- 地理位置(续)
- 本地存储
- 本地存储(续)
- 离线 Web 程序
- 离线 Web 程序(续)
- 表单
- 表单(续)
- 表单(续二)
- 可扩展性
- 可扩展性(续)
- 可扩展性(续二)
- 可扩展性(续三)
- 可扩展性(续四)
- 历史 API
历史 API
浏览器的地址栏可能是所有用户界面中最复杂的部分了。我们有关于账单的 URL,有火车票的 URL,还有关于街道信息的 URL。结合后退按钮——或许是浏览器最重要的按钮——你可以实现一种强大的前进、后退操作,在这个庞大的 Web 世界中自由来回。
HTML5 历史 API 是使用脚本维护浏览器历史的一种标准方法。这个 API 的一部分——历史导航——已经在早期版本的 HTML 中实现。HTML5 新增部分包括向浏览器历史添加历史记录,显式地改变浏览器地址栏(不需要页面刷新),以及当用户按下浏览器后退按钮,从堆栈中删除页面历史时发出事件等。这意味着浏览器地址栏的 URL 可以唯一标识当前资源,即使是在有很多脚本的、几乎永远不会刷新页面的大型应用程序中。
为什么?
为什么我们需要手动维护浏览器历史?毕竟,一个简单的链接就可以导航到一个新的 URL。这种实现已经在 web 中超过 20 年了,并且现在我们还是继续这种实现。历史 API 也并不是要推翻这种方式。恰恰相反,在最近几年中,web 开发人员找到一种新方式,不通过标准 API 的改变就可以实现 web 革命。HTML5 历史 API 就是为了让有着大量脚本的 web 应用程序能够更好实现。
回到最初的原则,URL 是干什么用的?用唯一标识一个资源。你可以直接连接到它,可以使用书签收藏,搜索引擎可以做索引;你可以把这个 URL 复制粘贴到邮件中发给别人,他只要点击这个 URL 就可以看到你想让他看的原始页面了。听起来不错,这就是 URL 的重要之处。
所以,我们希望唯一的资源有唯一的 URL。不过与此同时,浏览器却有一个限制:如果你改变了 URL,甚至是通过脚本,它就得向服务器发送请求,刷新整个页面。这很耗时,也耗资源。如果你仅仅想转到一个与当前页面类似的页面,这么做无疑很浪费。新页面的所有东西都得重新下载,即使那些与当前页面一模一样的东西。我们没有办法告诉浏览器改变 URL 但是仅仅下载一半页面。
HTML5 历史 API 能够让你这么做。在一定程度上,你可以通过脚本告诉浏览器,仅下载一半页面。这解释起来挺复杂,并且需要你做一些工作。下面来看看吧!
假设我们有两个页面,A 和 B。这两个页面 90%都是一样的,只有 10%不同。用户打开了 A 页面,然后想打开 B。这时,我们不需要整个页面的刷新,你需要截断这个导航,然后去做下面的工作:
- 从 B 页面下载与 A 页面不同的那 10%(可以通过 XMLHttpRequest)。这需要服务器端做一些修改。你需要重写代码,返回 B 页面与 A 页面不同的那 10%的代码。这可以使用一个隐藏 URL,或者用户看不到的查询参数。
- 替换改变部分(使用 innerHTML 或者其他 DOM 方法)。你可能也得修改被替换部分的节点的事件监听函数。
- 将浏览器地址栏切换成 B 页面的,使用 HTML5 历史 API 的一个函数即可。
在上面解释的最后,浏览器已经是页面 B 的 DOM,看起来就和直接打开页面 B 没什么两样。浏览器地址栏最后也变成页面 B 的 URL,就像你直接输入一样。但实际上你并没有真的到了页面 B,没有整页刷新。这就是后面隐藏的情况。但是,这个“编译后”的页面看起来就和页面 B 没什么两样,URL 也是页面 B,用户也不会感到什么不同(当然,也不会知道你为此而做的大量工作)。
怎么做?
HTML5 历史 API 是 window.history
对象的一系列方法,另外还有 window
对象的一个事件。你可以使用这些函数去测试浏览器是否支持历史 API。
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
– | 4.0+ | 5.0+ | 8.0+ | – | 4.2.1+ | – |
dive into dogs 是一个使用 HTML5 历史 API 的例子。这个页面是一个典型的例子:一篇长长的文章,配上一个关联的相册。在支持历史 API 的浏览器中,使用相册上面的 Next 和 Previous 链接可以更新相册,并且可以更新浏览器地址栏的 URL,不需要刷新这个页面。在不支持历史 API 的浏览器中——或者是原本支持历史 API,但是用户禁用了脚本的浏览器中——这些链接就是普通的链接,通过刷新整个页面达到新的页面。
这是很重要的一点:
如果浏览器因为禁用了脚本而导致你的应用系统错误,小心 Jakob Nielsen 的狗会出来跑到你屋里咬你的哦~~(意思是如果因为禁用脚本而到处原本的应用程序不可用,那这个历史 API 就不能向前兼容了,也就没什么应用价值了。)
下面我们深入学习一下这个页面,看看是它如何工作的。下面是一张照片是如何工作的:
<aside id="gallery"> <p> <a id="photonext" href="casey.html">Next ></a> <a id="photoprev" href="adagio.html">< Previous</a> </p> <figure id="photo"> <img id="photoimg" src="gallery/1972-fer-500.jpg" alt="Fer" width="500" height="375"> <figcaption>Fer, 1972</figcaption> </figure> </aside>
这里没有什么不同之处。照片使用 <figure>
中的 <img>
实现,链接则使用一个常规的 <a>
元素,而这所有的元素都是在一个 <aside>
中。注意,这些常规链接是能够正常工作的。以下所有代码都应该在脚本检测代码之后。如果用户使用的是不支持历史 API 的浏览器,我们的这些脚本都不应该执行。当然,如果用户禁用脚本,这些代码也不能执行。
主函数将遍历所有链接,将其传给一个函数 addClicker()
。这个函数将作为用户点击的实际回调动作。
function setupHistoryClicks() { addClicker(document.getElementById("photonext")); addClicker(document.getElementById("photoprev")); }
下面是 addClicker(
) 函数。它的参数是 <a>
元素,作用是为其添加点击处理函数。而这个处理函数是很有趣的。
function addClicker(link) { link.addEventListener("click", function(e) { swapPhoto(link.href); history.pushState(null, null, link.href); e.preventDefault(); }, false); }
swapPhoto()
函数实现我们前面解释中的两个步骤。 swapPhoto()
函数的前一半工作是获取 URL 自身——casey.html,adagio.html 等等——然后为隐藏的页面构建 URL,标记处我们要访问的下一张照片。
function swapPhoto(href) { var req = new XMLHttpRequest(); req.open("GET", "http://diveintohtml5.org/examples/history/gallery/" + href.split("/").pop(), false); req.send(null);
这是实际返回的结果: http://diveintohtml5.org/examples/history/gallery/casey.html (你可以用浏览器打开看看到底是什么东西)。
<p> <a id="photonext" href="brandy.html">Next ></a> <a id="photoprev" href="fer.html">< Previous</a> </p> <figure id="photo"> <img id="photoimg" src="gallery/1984-casey-500.jpg" alt="Casey" width="500" height="375"> <figcaption>Casey, 1984</figcaption> </figure>
看起来熟悉吗?的确是。这就是我们在原始页面中用于显示照片的代码。
swapPhoto()
函数的第二部分用户处理我们上面所说的第二步:将新下载的内容插入到当前页面。记住,有一个 <aside>
包围着整个 figure、photo 和 caption。所以,我们只需要一行代码:修改 <aside>
的 innerHTML 属性为 XMLHttpRequest 返回的 responseText 属性。
if (req.status == 200) { document.getElementById("gallery").innerHTML = req.responseText; setupHistoryClicks(); return true; } return false; }
(注意,我们使用了 setupHistoryClicks()
。这个函数用于重置新插入的导航链接的自定义 click 事件处理函数。设置 innerHTML 将会丢失任何旧的链接及其事件处理函数。)
现在,我们回到 addClicker()
函数。在成功交换照片之后,我们还得做一件事:设置浏览器地址栏 URL。
history.pushState(null, null, link.href);
history.pushState()
函数接受三个参数:
- state:JSON 数据结构。它将会传回给 popstate 事件处理函数。我们将在后面介绍这个函数。目前我们还没有用到这个函数。
- title:字符串。这个参数目前在主流浏览器中还没有实际应用。如果你想设置页面 title,你应当将其存储在 state 参数中,然后将其设置给 popstate 回调函数。
- url:URL。想要在浏览器地址栏显示的 URL。
设置 history.pushState
会立即改变浏览器地址栏的 URL。那么,这就是最后的步骤了吗?等等,还不是。我们还得讨论下当用户点击了后退按钮的时候将会发生什么。
一般的,当用户打开新的页面时(整页刷新),浏览器将新的 URL 压入浏览历史栈,然后下载并显示新的页面。当用户点击后退按钮时,浏览器从浏览历史栈中弹出一个页面,然后重绘前一个页面。但是,在我们避免了整页刷新的时候,这又该如何实现呢?你可以继续“向前移动”到一个新的 URL,那么,你就应该可以“向后移动”到一个以前的页面。关键的是实现“向后移动”的这个 popstate 事件。
window.addEventListener("popstate", function(e) { swapPhoto(location.pathname); }
使用 history.pushState()
函数,将 URL 压入浏览器的历史堆栈。用户点击后退按钮时,浏览器的 window 对象将发出 popstate 事件。这就是你的机会!将什么东西变没了也不是目的,你还得将它变回来。
在我们的例子中,“把它变回来”就像切换照片一样简单。我们仅需要使用当前位置调用 swapPhoto()
函数。在你的 popstate 回调调用的时候,浏览器地址栏变成改变之前的 URL。同时,全局的 location 属性也会被更新为之前的 URL。
为了更好的理解这个过程,我们从头浏览一下:
- 用户加载 http://diveintohtml5.org/examples/history/fer.html ,看到一篇故事和 Fer 的照片。
- 用户点击 “Next” 链接,这是一个
<a>
标签,其 href 值为 http://diveintohtml5.org/examples/history/casey.html 。 - 为了避免整页刷新,我们重新实现
<a>
的 click 回调函数,执行我们自己的代码。 - 自定义的 click 处理函数调用了
swapPhoto()
函数。这个函数创建了一个XMLHttpRequest
对象,异步下载位于 http://diveintohtml5.org/examples/history/casey.html 的 HTML 片段。 swapPhoto()
函数设置相册的innerHTML
属性(<aside>
元素),因此将照片换成了 Casey。- 最后,我们的自定义 click 处理函数调用了
history.pushState()
函数,手动改变浏览器地址为 http://diveintohtml5.org/examples/history/casey.html 。 - 用户点击浏览器后退按钮。
- 浏览器发现 URL 已经被手动压入浏览历史栈(通过
history.pushState()
函数)。为了避免直接打开上一个 URL 导致的整页刷新,浏览器仅仅更新地址栏为 http://diveintohtml5.org/examples/history/fer.html ,并发出一个 popstate 事件。 - 自定义的 popstate 处理函数再一次调用
swapPhoto()
函数,这一次,旧的 URL 已经出现在浏览器的地址栏了。 - 再一次使用
XMLHttpRequest
,swapPhoto()
函数下载位于 http://diveintohtml5.org/examples/history/gallery/fer.html 的 HTML 代码片段,并且设置<aside>
的innerHTML
属性。因此,页面的相册照片换成了 Fer 的。
这就是完整的解释。所有界面部分(当前页面,浏览器地址栏)都显示用户来到一个新的页面,又后退到旧的页面。但实际上,这一过程中并没有整页刷新,这就是我们的目的。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论