ajax在当代网站已取得异常普各处运用,重要的优点人人都晓得(异步加载数据,不必革新全部浏览器,更小的数据传输尺寸)。关于那些老网站或许老项目来讲通盘改形成ajax并不实际,因而就有了“部分页面革新”这个处理计划。假如不晓得“部分页面革新”是何物请看这里,这里和这里。
在我们的项目里,将本来的iframe
或许frame
统统替代成了时兴的div
,然后修正了页面上统统提议要求的处所,把相应内容jQuery.load
到div
里。
因而乎本来老旧的网站变成了一个时兴的基于ajax的网站,每一个页面传输的数据量变小了,再也不必处理使人头疼的:
为了消弭滚动条让iframe自适应大小
怎样接见parent window变量的题目(另有怎样接见parent的parent的parent… window变量的题目)
怎样接见child iframe里的变量[]的题目了(另有怎样接见child的child的child… iframe里的变量的题目)
由于人人永久都在同一个window里,而且div自身就会依据内容自动撑大。然则等等!浏览器怎样不能退却了?
我们的谁人项目是一个满大街可见的XX管理信息体系,这类体系最常见的计划就是左边一个树形菜单地区,右边是一个功用地区,功用地区里有一个查询前提地区(内里有个查询按钮),另有一个空缺的地区用来显现查询效果,同时是用户操纵数据的处所(比方form表单)。
在iframe时期,上面讲到的4个地区都是一个iframe,这也就意味着我们可以有很变态的退却才能。
固然了一般来讲用户最经常运用的就是对操纵地区做退却行动,比方查询一下,挑选一条纪录点击修正,看到form表单,修正一下,在点击保留前忏悔了,点击浏览器的退却,回到查询效果页面。
然则在引入了ajax后没法退却了,由于ajax要求不会纪录到浏览器汗青里,汗青都没有了天然就没法退却了。
幸亏Html5的History API可以协助我们处理题目。我们可以工资的运用history.pushState
来人造汗青信息,而且经由过程监听popstate
事宜来晓得用户点击了浏览器退却或行进按钮,然后将页面元素复原到汗青上的某个状况。关于Html5 History API的相干信息可以看这里。
然则事变远不止这么简朴,下面是我们碰到的一些坑:
圈套1:反复实行js剧本
// 点击查询按钮的时刻工资组织一个浏览器汗青
$('#some-button').click(function() {
$(targetSelector).load(url);
history.pushState({
container : targetSelector,
content : $(targetSelector).html()
}, null, url);
});
// 当浏览器退却后者行进的时刻,我们把当时的效果从新加载到container里来
window.addEventListener('popstate', function() {
var state = history.state
$(state.container).html(state.content);
})
统统看上去都OK,直到…我们发明部分页面革新所取得的效果里包含了操纵dom元素的js。
当碰到这类状况时会发作很巧妙的征象,history state.content是已加载终了+js实行后的效果,当我们从新复原的时刻,我们会把这个效果加载出来,而且又会实行一遍js。假如这个js是一个增加dom的行动那末在退却的时刻你会看到这个反复的dom元素。
我们想过跟踪哪些dom元素是被js修正过的来防止这个题目,然则…这是不实际的。
圈套2:没法复原到最初状况
前面的计划由于load的内容里能够有js剧本所以有严峻缺点,因而我们换了个思绪,history里保留responseText,而不是已load好后的东西。
// 点击查询按钮的时刻工资组织一个浏览器汗青
$('#some-button').click(function() {
$(targetSelector).load(url, function(responseText) {
history.pushState({
container : targetSelector,
content : responseText
}, null, url);
});
});
// popstate事宜的处理方式一样
然则依然碰到了这么一个题目,假如container(革新目的地区,某个div)本来是有内容的,而这个内容不是经由过程ajax部分页面革新而来,而是用户一进入这个页面就已有的,比方运用服务器端的模板引擎天生的页面,那末在它加载完html片断后就没法回退了。由于它的内容一开始就不在history里(事实上浏览器本身发生的history是没有state的),如许就形成了退无可退的局势。
假如你想,我们只需保留这个container本来的内容不就好了,当退却的时刻我们直接恢复它本来的内容,然则请看圈套1
不过当发作退无可退的状况时,我们认为已退回到了第一次进入页面的状况,这个时刻我们革新全部页面就好了。
圈套3:多个并列的container
圈套2的处理计划实际上是基于container之间是属于嵌套关联或许就一个container的状况的。假如是这类状况就不行了:
有A和B两个container,点击某个按钮革新了A的内容(发生汗青),然后在点击某个按钮革新的B的按钮(发生汗青),根据用户的料想状况,第一次退却复原B本来的内容,第二次退却复原A本来的内容。但实际上,第一次退却没法复原B的内容(圈套2),第二次退却页面革新了(统统恢复最初的模样)。
假如B是嵌套在A里的就无所谓了,第一次退却的时刻取得的是A的state,依据A的state复原A的内容的时刻顺便把B也复原了,第二次退却页面革新,把A也复原了。
而且依据圈套1所讲,我们也不能在history里存储A或许B里本来的内容。
处理办法:关于这类操纵就不要纪录汗青了。
圈套4:看到过期页面
我们在History state里存的是当时load时的responseText,当我们退却的时刻看到的是过期的页面,比方我们本来查询效果里看到有A纪录,然后我们跳转到其他页面里,然后再退却到查询效果页面看到A纪录还在,然则这个A纪录极能够只是一个幽魂,在数据库里早就已不存在了。假如我们这个时刻再对A纪录操纵就有涌现毛病。
处理办法是我们在history state里保留url已相干的参数,当popstate的时刻从新提议要求就好了,如许一来的话也减少了history存储state所须要的空间。
// 这里只给get要求的例子,post的道理也差不多
$('#some-button').click(function() {
$(targetSelector).load(url, function(responseText) {
history.pushState({
container : targetSelector,
url : url
}, null, url);
});
});
window.addEventListener('popstate', function() {
var state = history.state;
$(state.container).load(state.url);
});
圈套5:redirect
纵然我们在history state保留了url你就认为没事了?too simple, too naive!假如我们对这个url提议的要求被服务器redirect到另一个url,那末在history state里保留这个url就不对了。
假如我们这个url是用来删除某条纪录的,服务器收到要求在数据库里删除了这条纪录,然后redirect到了首页url,那末这个时刻你在history里应当存谁人url呢?显然是首页的url,由于假如你存了删除url,那末在退却的时刻,我们会从新提议这个url,想一想这多吓人。
处理办法实在不太简朴,由于ajax是不是被redirect你是不晓得的,用jQuery封装的jqXHR对象也没法晓得这个。
或许链WHATWG的XmlHttpRequest.responseURL可以救你,然则浏览器兼容性不好。
我的做法在服务器sendRedirect
之前在requestUrl
的queryString
里增加一个flag,用一个特地的servlet filter推断过来的要求是不是有这个flag,假如有那末就将本次要求的url(也就是redirect到的url)放到response
的一个特定的header里。然后就可以用jqXHR.getResponseHeader('some-header')
来取得这个url,把这个url放到history state里。
圈套6:没法准确复原dom对象的状况
不论是保留responseText照样保留url要求参数,都没法在浏览器退却的时刻准确复原dom对象的状况,比方我在IE6里有个如许的特征,你在某个页面勾选了某个checkbox,然后跳转到一个新的页面然后再退却,谁人checkbox照样处于勾选状况,这个在应用ajax部分页面革新里是完整做不到的,想到用户和我说之前退却的时刻谁人勾还在如今勾没有了,不处理这个BUG就不验收的事变时才想到iframe的好啊。
所以假如要准确复原dom对象的状况,得在history.pushState的时刻自行把相干信息保留下来,在popstate的时刻用到这些信息并复原dom。
事实上即运用了iframe也并非统统的浏览器会复原dom对象状况,看这篇文章。
总结
不要随意马虎从iframe切换到ajax部分页面革新
要本身掌握那些ajax部分页面革新纪录汗青,哪些不纪录,有些时刻能够还须要replaceState,不要想固然的把统统要求都纪录汗青
把代码改形成ajax部分页面革新只是第一步,还须要对全部网站、运用的UI做计划和设想,关于这个题目不存在通用的处理计划