#246 AJAX History State
- Download:
- source codeProject Files in Zip (123 KB)
- mp4Full Size H.264 Video (21.3 MB)
- m4vSmaller H.264 Video (12.6 MB)
- webmFull Size VP8 Video (29 MB)
- ogvFull Size Theora Video (28.1 MB)
在第240集 [ 观看 , 阅读 ]我们向你展示了如何使用AJAX来添加排序,分页和搜索到产品表。这样使用AJAX的问题之一是,当我们进行排序或筛选表格的时候,该网页的网址不变。这意味着我们不能使用返回按钮撤销我们对表格的修改,我们也不能把排序或筛选后的页面存为书签,以便我们回来后得到同样的结果。
这是把AJAX功能添加到一个站点时一个常见的问题,如果我们能找到一个解决办法这将会很有用。我们会在这集中告诉你怎么做。
介绍history.pushState方法
我们我们曾经在第175 [ 观看 , 阅读 ]集中涉及过这个话题,在那一集中,我们通过改变URL在一个启用AJAX的网站加入书签和历史记录的功能。在那集中,我们分页后就在URL后加入了一个锚点,比如说,第3页,网址是http://localhost:3000/#page=3
。这使我们能够书签页,使后退按钮正常工作。这种方法效果很好,但很快就变得更复杂的情况复杂,比如像现在需要在排序和搜索的同时进行分页。
我们将在这里使用的技术也被应用在GitHub的文件浏览器上。当我们在浏览器中点击了一个GitHub的一个文件夹的时候,这个页面就通过AJAX刷新了,并且不仅仅是锚点整个URL也被JavaScript更新了。这是一个更优雅的解决方案,这意味着我们可以刷新页面或存为书签。另外,该页面被添加到浏览器的历史中,这样后退按钮就像我们期待的那样工作了。
GitHub在博客上讲过这个方案,这个blog后很值得一读,看看这是如何实现的。要看到它工作你需要最新版的Safari,Chrome或Firefox,但它是一个绝对值得使用的技术。是history
对象的pushState
和replaceState
方法和popstate
事件让这个”神奇”工作的。在GitHub的博客上有些有用的链接,从那你可以得到更细节的关于这个函数使用的方法的演示。
让我们看看把整个功能加到我们简单的购物应用程序是多么的简单,以便它能在我们排序,搜索和分页产品表的时候起作用。我们添加到原来的应用的JavaScript程序是这样的:
``` /public/javascripts/application.js $(function () { $('#products th a, #products .pagination a').live('click', function () { $.getScript(this.href); return false; } ); $('#products_search input').keyup(function () { $.get($('#products_search').attr('action'), $('#products_search').serialize(), null, 'script'); return false; }); }) ```如果上面的代码中包含你觉得不熟悉的东西,那么它值得去240集查看的更多细节。我们使用jQuery在这里,但因为用pushState
和replaceState
,这种技术也可以和Prototype使用。
代码的第一部分用于处理排序和分页的AJAX功能。我们将添加一个调用history.pushState
的代码,这样当一个分页或排序的链接被点击后,URL就被添加到历史记录里了。
有一些有用的关于pushState
文档在Mozilla的开发者网上,表明它有三个参数。第一个参数是状态对象,它可以将任何对象,他会当popState事件被触发的时候返回回来。第二个参数是标题,而第三个是URL。我们知道这些后,就可以添加pushState
的调用了。
我们并不需要存储任何状态,所以我们在这里传递null
作为第一个参数。同样,我们并不需要一个标题,所以我们传递一个空字符串到第二个参数。对于URL我们传递了当前点击的链接this.href
。
如果我们现在刷新页面,然后点击AJAX排序或分页链接的后,那个表格会在没有重新加载页面的情况下更新,但现在每次我们点击一个链接的时候,当前在地址栏上的URL会变成我们传入pushState
方法的URL,同时这个URL会被添加到浏览器的历史记录中。
所以,现在我们可以把排序和分页后的表格存为书签,当我们回来,或重新载入这个页面,即使页面被通过AJAX动态更新,分页和排序是可以回复的。但是后退按钮还不会像我们期待的那样工作,因为我们还没有监听popstate
事件。我们可以通过在popstate
事件触发后触发一个方法来解决这个问题
在popstate
事件触发时,URL已经改回以历史记录中的前一个,所以我们可以通过调用$.getScript
方法并传入URL来更新产品表格。
如果我们现在刷新页面并分页和排序几次,我们会发现回退按钮已经工做了,我们可以在我们所做的更改中导航回来。
搜索
下面我们要看一下搜索功能。如果我们在我们现在的应用中对产品搜索时,它会过滤的结果,但是URL不会改变,所以如果在我们搜索后把当前页存为书签或刷新后,我们将失去了这个搜索页面。
用AJAX搜索的JavaScript是这样的:
``` /public/javascripts/application.js $(function () { // Other functions omitted. $('#products_search input').keyup(function () { $.get($('#products_search').attr('action'), $('#products_search').serialize(), null, 'script'); return false; }); }) ```我们将在这里用和我们更改排序部分代码同样的方式使用pushState
。
pushState
记录的URL是把一个组合形式搜索表单的action
和序列化后表单数据用问号连接起来后的一个有效的网址。
当我们刷新页面,就看到每当我们在搜索框里键入时URL都会改变。然而我们按返回键,发现历史页面是我们在搜索框里每次输入的字母,这样是不不理想的。这是因为我们是在每一个键被按下的时候调用的pushState
。幸运的是我们能够很简单的做到这一点,通过改变上面代码中的pushState
为replaceState
。这样我们每次替换当前的状态而不是在方法被调用时假如另一个状态。
如果我们打开一个新的窗口做一个搜索,我们将看到URL的变化,但这种改变不会添加到浏览器的历史记录中去。
这是功能不像我们想的那么理想,我们在用户开始搜索的时候使用pushState
,随后调用replaceState
,以便搜索项被添加到历史记录中。这个超出了一点这一集的范围,因此我们将不会在这里涉及。
给每个状态添加标题
如果我们用几个不同的方式排序产品,然后单击并按住后退按钮去查看浏览器的历史,它只会显示每一次更改的URL,这样当我们要会退的时候就很难选择了。如果一个标题而不是URL显示在界面上就会让历史记录的功能更有用。我们通过每次调用pushState
的时候把网页的标题设置为第二个参数来实现。
我们如何设置有用网页的标题?我们将要做的是设置页面标题,然后在pushState中使用这个标题。我们可以利用当一个AJAX请求完成后发回更新用的index.js.erb
文件设置设置标题。
我们将设置包含搜索和排序条件为当前页面的标题后,所有相关的信息就可以看得见了。然后,我们可以修改pushState
和replaceState
的调用,让它们更新页面的标题。
现在,我们每次搜索或排序的时候网页的标题都会改变,如果我们看浏览器的历史,我们会看到标题的列表而不是URL。
旧浏览器的处理
我们应用程序的新功能工作的非常好,但我们一直假设history.pushState
方法在用户的浏览器中是可用的,但是好像只有最新版的Safari,Firefox和Chrome支持这个特性。为了处理所有的浏览器,首先要检查该浏览器是否支持pushState,然后修改我们的应用程序。
我们会做的是检查是判断history
对象和0}history.pushState方法是不是存在,如果不是,我们会关闭所有AJAX的功能,我们的程序使用链接而不是AJAX,这样就可以优雅的降级。
这就是这集的全部内容。它没有使用Rails的特定处理方式去处理Rails应用程序中的AJAX。如果能够改变这样的网址,我们就能像对待传统的HTML链接一样对待AJAX链接。