• Javascript history pushState onpopstate方法做AJAX SEO


    参考MDN:

    https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_history

    https://developer.mozilla.org/zh-CN/docs/Mozilla_event_reference/popstate

     window 对象通过history对象提供对览器历史记录的访问能力。它暴露了一些非常有用的方法和属性,让你在历史记录中自由前进和后退,而在HTML5中,更可以操纵历史记录中的数据。

    历史记录概览

    可以通过back(),forward()和go()方法在用户的历史记录中前进与后退。

    前进与后退

    在历史记录中后退,可以这么做:

    window.history.back();
    

    这就像用户点击浏览器的后退按钮一样。

    类似的,你可以前进,就像在浏览器中点击前进按钮,像这样:

    window.history.forward();
    

    移动到指定的历史记录点

    通过指定一个相对于当前页面位置的数值,你可以使用go()方法从当前会话的历史记录中加载页面(当前页面位置索引值为0,上一页就是-1,下一页为1)。

    要后退一页(相当于调用back()):

    window.history.go(-1);
     

    向前移动一页(相当于调用forward()):

    window.history.go(1);
     

    类似的,传递参数“2”,你就可以向前移动2页。

    你可以查看length属性值,了解历史记录栈中一共有多少页:

    var numberOfEntries = window.history.length;
    
    注意: IE中可以给go()方法传递URL字符串参数,这是非标准的方法,并且Gecko不支持。

    添加和修改历史记录条目

    HTML5引进了history.pushState()方法和history.replaceState()方法,它们允许你逐条地添加和修改历史记录条目。这些方法可以协同window.onpopstate事件一起工作。

    使用 history.pushState() 会改变 referrer 的值,而在你调用方法后创建的 XMLHttpRequest 对象会在 HTTP 请求头中使用这个值。referrer的值则是创建 XMLHttpRequest 对象时所处的窗口的URL。

    案例

    假设 http://mozilla.org/foo.html 将执行如下JavaScript代码:

    var stateObj = { foo: "bar" };
    history.pushState(stateObj, "page 2", "bar.html");
    

    这将让浏览器的地址栏显示http://mozilla.org/bar.html,但不会加载bar.html页面也不会检查bar.html是否存在。

    假设现在用户导航到了http://google.com,然后点击了后退按钮,此时,地址栏将会显示http://mozilla.org/bar.html,并且页面会触发popstate事件,该事件中的状态对象(state object)包含stateObj的一个拷贝。该页面看起来像foo.html,尽管页面内容可能在popstate事件中被修改。

    如果我们再次点击后退按钮,URL将变回http://mozilla.org/foo.html,文档将触发另一个popstate事件,这次的状态对象为null。回退同样不会改变文档内容。

    pushState()方法

    pushState()有三个参数:一个状态对象、一个标题(现在会被忽略),一个可选的URL地址。下面来单独考察这三个参数的细节:

    • 状态对象(state object) — 一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。

      任何可序列化的对象都可以被当做状态对象。因为FireFox浏览器会把状态对象保存到用户的硬盘,这样它们就能在用户重启浏览器之后被还原,我们强行限制状态对象的大小为640k。如果你向pushState()方法传递了一个超过该限额的状态对象,该方法会抛出异常。如果你需要存储很大的数据,建议使用sessionStorage或localStorage。

    • 标题(title) — FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。

    • 地址(URL) — 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。

    注意: 在 Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 至 Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 中,传入的对象使用JSON来进行序列化。从 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)开始,对象使用结构化拷贝算法来进行序列化。这将允许更多类型的对象能够安全传入。

    某种意义上,调用pushState()有点类似于设置window.location='#foo',它们都会在当前文档内创建和激活新的历史记录条目。但pushState()有自己的优势:

    • 新的URL可以是任意的同源URL,与此相反,使用window.location方法时,只有仅修改 hash 才能保证停留在相同的document中。
    • 根据个人需要来决定是否修改URL。相反,设置window.location='#foo',只有在当前hash值不是foo时才创建一条新历史记录。
    • 你可以在新的历史记录条目中添加抽象数据。如果使用基于hash的方法,你只能把相关数据转码成一个很短的字符串。

    注意pushState()方法永远不会触发hashchange事件,即便新的地址只变更了hash。

    replaceState()方法

    history.replaceState()操作类似于history.pushState(),不同之处在于replaceState()方法会修改当前历史记录条目而并非创建新的条目。

    当你为了响应用户的某些操作,而要更新当前历史记录条目的状态对象或URL时,使用replaceState()方法会特别合适。

    注意: 在 Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 至 Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 中,传入的对象使用JSON来进行序列化。从 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)开始,对象使用结构化拷贝算法来进行序列化。这将允许更多类型的对象能够安全传入。

    popstate事件

    每当激活的历史记录发生变化时,都会触发popstate事件。如果被激活的历史记录条目是由pushState所创建,或是被replaceState方法影响到的,popstate事件的状态属性将包含历史记录的状态对象的一个拷贝。

    案例见 window.onpopstate

    读取当前状态

    在页面加载时,可能会包含一个非空的状态对象。这种情况是会发生的,例如,如果页面中使用pushState()或replaceState()方法设置了一个状态对象,然后用户重启了浏览器。当页面重新加载时,页面会触发onload事件,但不会触发popstate事件。但是,如果你读取 history.state 属性,你会得到一个与  popstate 事件触发时得到的一样的状态对象。

    你可以直接读取当前历史记录条目的状态,而不需要等待popstate事件:

    var currentState = history.state;
    

    浏览器兼容性

    FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
    replaceState, pushState 5 4.0 (2.0) 10 11.50 5.0
    history.state 18 4.0 (2.0) 10 11.50 6.0

    你可能需要History.js来解决跨浏览器兼容性问题

    其他资料

    如果你玩过Google+,看到过YouTube的新界面,便会体验到这个HTML5的新功能。使用pushState + Ajax(pjax),可以实现网页的ajax加载,同时又能完成URL的改变而没有网页跳转刷新的迹象,就像是改变了网页的hash(#)一样。

    旧的解决方案

    曾说SEO和ajax是天敌。此前从Twitter开始流行Ajax+hash的方式调用内容,Google给出的解决方案是“#!~string”自动转换为“?_excaped_fragment_=~string”来抓取动态内容。但这无疑会非常麻烦:首先你需要对网站进行“?_excaped_fragment_=~string”的处理配置,而且,如果用户把网址“http://example.com/#!/~string”直接复制并分享的话,意味着网页还必须监听hashchange。不过如果你觉得这个#!很好看就没关系了。

    twtter hash

    新的解决方案: pushState

    然而HTML5的新接口pushState / replaceState就可以比较完美的解决问题,它避免了改变hash的问题,避免了用户不理解URL的形式感到疑惑,同时还有onpopstate提供监听,良好响应后退前进。而且它不需要这个URL真实存在。

    HTML5 的 pushState+Ajax

    HTML5提供history接口,把URL以state的形式添加或者替换到浏览器中,其实现函数正是 pushState 和 replaceState。

    pushState 例子

    pushState() 的基本参数是:

    window.history.pushState(state, title, url);

    其中state和title都可以为空,但是推荐不为空,应当创建state来配合popstate监听。

    例如,我们通过pushState现改变URL而不刷新页面。

    var state = ( {

    url: ~href, title: ~title~additionalKEY~additionalVALUE

    } );

    window.history.pushState(state, ~title~href);

    其中带有“~”符号的是自定义内容。就可以把这个~href(URL)推送到浏览器的历史里。如果想要改变网页的标题,应该:

    document.title= ~newTitle;

    注意只是pushState是不能改变网页标题的哦。

     

    replaceState 同理

    window.history.replaceState( state, ~title, ~href);

    pushState、replaceState 的区别

    pushState()可以创建历史,可以配合popstate事件,而replaceState()则是替换掉当前的URL,不会产生历史。

    限制因素

    只能用同域的URL替换,例如你不能用http://baidu.com去替换http://google.com。而且state对象不存储不可序列化的对象如DOM。

    Ajax 配合 pushState 例子

    现在用Ajax + pushState来提供全新的ajax调用风格。以jQuery为例,为了SEO需要,应该为a标签的onclick添加方法。

    $("~target a").click(function(evt){

    evt.preventDefault(); // 阻止默认的跳转操作

    var uri=$(this).attr('href');

    var newTitle=ajax_Load(uri); // 你自定义的Ajax加载函数,例如它会返回newTitle

    document.title=newTitle; // 分配新的页面标题

    if(history.pushState){

    var state=({

    url: uri, title: newTitle

    });

    window.history.pushState(state, newTitle, uri);

    }else{ window.location.href="#!"+~fakeURI; } // 如果不支持,使用旧的解决方案

    return false;

    });

    function ajax_Load(uri){ ... return newTitle; } // 你自定义的ajax函数,例如它会返回newTitle

    即可完成pushState。至于新标题newTitle的获取就是另外的问题了,例如你可以为a标签分配data-newtitle=~title属性并届时读取,或者如果你用的$.ajax()函数,可以用$(result).filter("title").text()来获取。

    另外如果需要对新加载的页面的连接同样使用这个ajax,则需要对新内容的a标签重新部署,例如

    $("~newContentTarget a").click(function(evt){ ... });

    pushState 配合 popstate 监听

    想要良好的支持浏览器的历史前进后退操作,应当部署popstate监听:

    window.addEventListener('popstate', function(evt){

    var state = evt.state;

    var newTitle = ajax_Load(state.url); //你自定义的ajax加载函数,例如它会返回newTitle

    document.title=newTitle;

    }, false);

    提醒,你可以通过setRequestHeader()来让服务器端配合你的ajax请求输出专门的内容。

     

    jQuery + PJAX 插件

    已经在github上发布,有人把PJAX做成了jQuery插件,方便调用,节省大量代码:

    if ($.support.pjax) {

    $(document).on('click', 'a[data-pjax]', function(event) {

    var container = $(this).closest('[data-pjax-container]')

    $.pjax.click(event, {container: container})

    });}

  • 相关阅读:
    结对 总结
    ”耐撕“团队 2016.3.29 站立会议
    词频统计 List Array
    基本数据结构简述
    深入理解HashMap
    常用排序算法Java实现
    Spring核心组件知识梳理
    HashMap中使用自定义类作为Key时,为何要重写HashCode和Equals方法
    Nginx是什么东东?
    Java中常用的四种线程池
  • 原文地址:https://www.cnblogs.com/sunshq/p/4228033.html
Copyright © 2020-2023  润新知