使用HTML5的History API

  HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的状况下修改站点的URL。这个功能颇有用,例如经过一段JavaScript代码局部加载页面的内容,你但愿经过改变当前页面的URL来反应出页面内容的变化,这时该功能能够派上用场。css

  举个例子,当用户从首页进入帮助页面时,咱们经过Ajax来加载帮助页面的内容。而后这个用户又转到产品页面,咱们须要再一次经过Ajax请求来替换页面的内容。当用户想分享页面的URL时,经过History API,咱们能够改变页面的URL来反应内容的修改,这样不论是用户分享仍是保存的URL都能和页面的内容对应起来。html

基本知识

  要查看这个API提供了哪些功能很是简单,打开浏览器的Developer Tools工具面板,而后在console中输入history。若是你的浏览器支持History API,你将会看到这个对象下面附带了不少方法。html5

  注意其中的pushStatereplaceState这两个方法。咱们能够在console中进行一些简单的测试,来看看当咱们使用这两个方法时URL会发生什么变化。稍后咱们将分析这两个方法中的全部参数,如今咱们只须要关注最后一个参数:jquery

history.replaceState(null, null, 'hello');

  上面代码中的replaceState方法改变了当前页面的URL,在后面添加了一个'/hello'。不过并无发出任何request请求,当前窗口仍然停留在以前的页面。不过这里有个问题,当你点击浏览器的后退按钮时,页面并不会回退到咱们经过replaceState方法修改以前的那个URL,而是直接回退到了上一个页面(即咱们进入到这个页面以前的那个页面)。这是由于replaceState方法不会修改浏览器的history,它只是简单地替换了地址栏中的URL。git

  要解决这个问题咱们须要使用pushState方法:github

history.pushState(null, null, 'hello');

  如今再点击浏览器的后退按钮,你会发现它和你预想的效果同样。由于pushState方法将咱们传给它的URL添加到浏览器的history中,从而改变了浏览器的history。假如咱们将另一个完整的站点URL传递给它会发生什么状况呢?例如咱们在baidu.com的首页进行测试,而后在console中输入下面的内容。web

history.pushState(null, null, 'https://twitter.com/hello');

  浏览器会报错。由于传递给pushState方法的URl必须和当前页面的URL属于同一个源(即不能跨域),不然会有很大的安全漏洞,开发人员可能会借用该功能来欺骗用户,让他们以为本身是在访问一个彻底不一样的站点,而事实并不是如此。api

  来看看传递给pushState方法的全部参数:跨域

history.pushState([data], [title], [url]);
  1. 第一个参数用来传递咱们须要的数据,当页面的状态发生变化时咱们能够接收到该数据。如用户点击浏览器的后退和向前按钮。须要注意的是在Firefox中只容许传递最多640K的数据。
  2. 第二个参数title是一个字符串,不过截止到目前,几乎全部的浏览器都忽略该参数。
  3. 最后一个参数是咱们想要替换的URL。

简单回顾一下

  这些History API最主要的功能就是不从新加载页面。以往咱们只能经过改变window.location的值来修改当前页面的URL,不过这会致使整个页面被从新加载。若是你修改的只是URL中的hash,则不会致使页面被刷新。 浏览器

  使用旧的hashbang方法能够改变页面的URL而不刷新页面。著名的Twitter就是使用的该方法,不过也广受诟病,毕竟hash在location中并不被做为一个真正的资源来对待。

  做为History API的早期支持者,Twitter后来抛弃了传统的hashbang方法。在2012年,Twitter的团队介绍了他们的新方法,并列出了其中的一些问题同时还详细地介绍了各浏览器应该如何实现该规范。

一个使用pushState和Ajax的例子

https://css-tricks.com/examples/State/

  在该示例中,咱们但愿用户经过咱们的网站找到电影捉鬼敢死队(一部美国电影)中的演员。当用户选择一个图片时,咱们须要在下方显示该演员对应的文字描述,同时给该图片一个被选中的效果。当点击后退按钮时,页面应该切换到上一个被选中的图片状态,同时图片下方的文字也要一并切换。当点击前进按钮时也同样。

  这里有一个效果图:

  这个示例的HTML代码很是简单:div.gallery中包含了全部的连接,每一个连接里有一个图片。接下来咱们放置了一个空的div.content,用来存放当演员图片被点击时显示在下放的文字。

<div class="gallery">
  <a href="https://cdn.css-tricks.com/peter.html">
    <img src="bill.png" alt="Peter" class="peter" data-name="peter">
  </a>
  <a href="https://cdn.css-tricks.com/ray.html">
    <img src="ray.png" alt="Ray" class="ray" data-name="ray">
  </a>
  <a href="https://cdn.css-tricks.com/egon.html">
    <img src="egon.png" alt="Egon" class="egon" data-name="egon">
  </a>
  <a href="https://cdn.css-tricks.com/winston.html">
    <img src="winston.png" alt="Winston" class="winston" data-name="winston">
  </a>
</div>

<p class="selected">Ghostbusters</p>
<p class="highlight"></p>

<div class="content"></div>

  若是没有JavaScript该页面仍然能够正常工做,点击图片能够跳转到对应的页面,而后点击后退按钮也能够回到以前的页面。这是为了考虑页面的可访问行和优雅降级。

  接下来咱们要添加JavaScript代码了。咱们经过event propagation给div.gallery元素中的每个link添加一个事件处理程序,像这样:

var container = document.querySelector('.gallery');

container.addEventListener('click', function(e) {
  if (e.target != e.currentTarget) {
    e.preventDefault();
    // e.target is the image inside the link we just clicked.
  }
  e.stopPropagation();
}, false);

  在if语句中,咱们获取到被选中图片的data-name属性的值,而后将'.html'添加到后面拼成一个要访问的页面地址,并将其做为第三个参数传递给pushState方法(不过在真实的例子中咱们可能会在Ajax请求成功以后才会去修改URL)。

var data = e.target.getAttribute('data-name'),
url = data + ".html";
history.pushState(null, null, url);
    
// 此处更改当前的classes样式
// 而后使用data变量的值更新
// 并经过Ajax请求.content元素的内容
// 最后再更新当前文档的title

(固然,此处咱们也能够直接使用link的href属性的值)

  我将真实代码中的内容都替换成注释了,这样咱们能够只关注pushState方法的使用。

  如今咱们点击图片,URL和Ajax请求的内容会被自动更新,可是当咱们点击后退按钮时并不会回退到以前选中的演员图片。这里咱们还须要在用户点击后退和前进按钮时使用另一个Ajax请求来更新内容,并再一次使用pushState方法来更新页面的URL。

  咱们使用pushState方法中的第一个参数(其中的state)来保存状态信息:

history.pushState(data, null, url);

  上面代码中的data参数在popstate事件触发时能够被获取到。当浏览器的后退和前进按钮被点击时会触发popstate事件。

window.addEventListener('popstate', function(e) {
  // e.state表示上一个被点击的图片的data-attribute
});

  咱们能够经过该参数传递一些咱们须要的信息,例如在该示例中咱们将以前选中的捉鬼敢死队的演员做为参数传递给requestContent方法,在该方法中,咱们使用jQuery的load方法进行一次Ajax请求。

function requestContent(file) {
  $('.content').load(file + ' .content');
}

window.addEventListener('popstate', function(e) {
  var character = e.state;

  if (character == null) {
    removeCurrentClass();
    textWrapper.innerHTML = " ";
    content.innerHTML = " ";
    document.title = defaultTitle;
  } else {
      updateText(character);
      requestContent(character + ".html");
      addCurrentClass(character);
      document.title = "Ghostbuster | " + character;
  }
});

  若是用户点击了演员Ray的图片,event listener会被触发,而后在pushState事件中保存图片的data属性的值。当用户点击另一个图片,并点击了浏览器的后退按钮,此时popstate事件会被触发,从而从新加载ray.html页面。

  这意味着什么呢?当咱们点击一个演员的图片而后将被更改的URL分享出去,用户访问这个URL时对应的HTML文件会被自动加载进来。这会带来一些更好的用户体验,并保证了URL和页面内容的一致性从而减小了所以而带给用户的一些困惑。

  上面的示例只是简单地经过jQuery来动态加载内容,咱们固然也能够在pushState方法中传递一些更加复杂的对象。不过这个例子已经能足够说明问题并帮助咱们开始学习如何使用History API的功能。咱们先要学会走,而后才能跑。

下一步

  若是咱们想大范围地使用这种技术,咱们应该考虑使用一些专有的工具,例如pjax 它是一个jQuery的插件,使用它能够大大提升咱们同时使用Ajax和pushState方法进行开发的速度,不过它只支持那些使用History API接口的现代浏览器。

  History JS能够兼容旧浏览器,对于不支持History API接口的浏览器,它依然使用旧的URL hash的方式来实现一样的功能。

有关URLs

  这里我特别引用了Kyle Neath有关URLs的说明:

URLs是一个通用的概念,它能够工做在Firefox, Chrome, Safari, Internet Explorer, curl, wget,甚至在你的iPhone, Android以及便签纸上。它是web中的一个通用的语法。不要认为这是理所固然的。任何一个稍微懂点技术的用户均可以浏览你的应用的90%以上的部分而不用去刻意记住那些URL的结构。要实现这样的效果,你须要考虑URLs的实用性。

  这意味着不论你想要进行什么样的hacks或性能优化,做为web开发人员,你应该注重URL。而随着HTML5 History API的帮助,咱们能够轻松地解决诸如上述示例中的一些问题。

常见问题

  • 将Ajax请求的地址嵌入到a标记的href属性中一般是个不错的主意。
  • 确保在JavaScript的click事件处理程序中return true,这样当有人使用中键点击或命令点击时不会致使程序被意外覆盖。

补充

浏览器支持

Chrome Safari Firefox Opera IE Android iOS
31+ 7.1+ 34+ 11.50+ 10+ 4.3+ 7.1+

原文地址:https://css-tricks.com/using-the-html5-history-api/

相关文章
相关标签/搜索