重新思考动态页面替换内容

Avatar of Jesse Shawl
Jesse Shawl

DigitalOcean 为您的旅程的每个阶段提供云产品。从 免费试用 200 美元的信用额度! 开始。

以下内容是来自 Jesse Shawl 的客座文章。

在 2012 年 5 月,Chris 更新了之前关于 动态页面替换内容 的文章。这篇文章是对该更新的更新,它使用 HTML5 历史记录 API 来提供更好的用户体验。

以下是最佳实践的简要回顾

  1. 在禁用 JavaScript 时也能正常工作。
  2. 可以“深层链接”到特定内容。
  3. 浏览器的后退按钮和前进按钮按预期工作。

URL 哈希的问题

对于单个用户来说,现有演示 很好地满足了标准,但 URL 是永久地址,它们会被共享。

考虑以下场景

  1. 我有一个带有启用 JavaScript 的精美浏览器。我在浏览演示站点,发现了一款很棒的产品,我想与朋友分享。
  2. 我复制了 URL “http://example.com/#awesome-product”,并发送给我的朋友。
  3. 我的朋友没有启用 JavaScript。她在浏览器中打开链接,发现“awesome-product”没有按预期加载,感到困惑。
  4. 她感到困惑/沮丧,并发誓再也不访问 example.com。

这是一种糟糕的用户体验!

今天,我们将改进现有的演示,使动态页面替换内容不再依赖于哈希。

查看演示   下载文件

Modernizr 用于渐进增强

注意:以下示例基于之前的演示构建。下载文件 这里 以便跟着操作。

如果你还没有使用 Modernizr,那就去下载它吧(我会等你的)。它是使用 JavaScript 检测浏览器功能的最简单方法。

由于我们将使用 HTML5 历史记录 API,因此我们只需要选中“历史记录”复选框。从 这里 下载自定义构建。

将它包含在 HTML 文件的 <head>

<script src='js/modernizr.js'></script>

测试 HTML5 历史记录支持非常简单

// dynamicpage.js

$(function() {
    if (Modernizr.history) {
        // history is supported; do magical things
    } else {
        // history is not supported; nothing fancy here
    }
});

首先,我们将设置所有内容来操作浏览器的历史记录,然后添加来自之前演示的所有精美加载内容。

使用 HTML5 历史记录 API 操作历史记录

HTML5 的 history.pushState() 方法允许我们

  1. 更改 URL
    • 不使用哈希
    • 不刷新页面(这是动态页面替换内容发生的地方)
  2. 更新浏览器的历史记录栈
    • 这样我们就可以使用后退和前进按钮点击浏览历史记录。

pushState() 方法接受三个参数

history.pushState(stateObject, "title", URL);

在本示例中,我们只提供 URL,但你可以在 Mozilla 开发者网络 上了解有关历史记录 API 的更多信息。

更改 URL 后,我们将需要设置一个函数来加载内容 - loadContent() 似乎是一个不错的名称。

$(function() {

  if (Modernizr.history) {

    // history is supported; do magical things

    // hijack the nav click event
    $("nav").delegate("a", "click", function() {

      _href = $(this).attr("href");

      // change the url without a page refresh and add a history entry.
      history.pushState(null, null, _href);

      // load the content
      loadContent(_href); // fear not! we're going to build this function in the next code block

    });

  } else {

    // history is not supported; nothing fancy here

  }

});

现在,我们只需要编写 loadContent() 函数,这只需从原始示例中获取代码即可。

代码片段

// set up some variables
var $mainContent = $("#main-content"),
    $pageWrap    = $("#page-wrap"),
    baseHeight   = 0,
    $el;

// calculate wrapper heights to prevent jumping when loading new content
$pageWrap.height($pageWrap.height());
baseHeight = $pageWrap.height() - $mainContent.height();

function loadContent(href) {

  $mainContent
    .find("#guts")
    .fadeOut(200, function() { // fade out the content of the current page
      $mainContent
        .hide()
        .load(href + " #guts", function() { // load the contents of whatever href is
          $mainContent.fadeIn(200, function() {
            $pageWrap.animate({
              height: baseHeight + $mainContent.height() + &quot;px&quot;
            });
         });
      
      $("nav a").removeClass("current");

      $("nav a[href$='" + href + "']").addClass("current");

    });

  });

}

处理浏览器后退和前进按钮点击

此时,内容以一种精美的 Ajax 方式加载,但点击后退按钮不会让我们返回……还没有。

历史记录 API 使我们能够访问 popstate 事件,该事件在历史记录栈发生变化时触发(即点击后退和/或前进按钮)。每当此事件触发时,我们只需要调用 loadContent() 函数即可。

$(window).bind("popstate", function() {
    link = location.pathname.replace(/^.*[\\/]/, ""); // get filename only
    loadContent(link);
});

一个小作业

在撰写本文时,popstate 事件在 Chrome 中页面加载时发生。这意味着正在发出两个请求

  1. 对 whateverpage.html 的原始 http 请求
  2. 由我们 loadContent() 函数中的 $.load 发出的请求

有几种不同的方法可以处理这个问题,但我让你决定哪种方法最适合你。