使用History API的后退和前进按钮

8
我有一个测试应用程序:http://dev.driz.co.uk/ajax/ 您可以使用jQuery AJAX点击链接来加载其他页面,其中有两种类型,globalTabs和localTabs,它们将内容加载到不同的面板中。请注意:每个页面实际上都是完整页面,但我们只是使用jQuery find方法获取我们想要的内容。
为了增强功能,我正在使用HTML5 History API(跨浏览器使用History.js)。
标题和URL更改良好,并且历史记录堆栈已成功推送,当我使用后退和前进按钮时,URL和标题确实恢复了。
现在,复杂的部分是从先前状态加载回内容,以及更复杂的是加载正确类型的内容,例如全局或本地ajax。为了实现这一点,我认为需要同时传递数据和类型给pushState,以便可以在popstate中再次重用它...
有人能帮助我指出正确的方向吗?我已经完成了所有第一部分的工作,只需要让它将ajax类型传递给pushState,然后在popstate更改时重新使用它。
为了改进它,我按照以下方式进行了重写,展示了我传递类型和数据的计划:
注意:我的大写“History”是因为我使用了History.js。
var App = {

    init: function() {

        $(document).ready(function() {

            $('.globalTabs li a').live('click', function (e) {
                e.preventDefault(); 
                App.globalLoad( $(this).attr('href') );
            }); 

            $('.localTabs li a').live('click', function (e) {
                e.preventDefault();
                App.localLoad( $(this).attr('href') );
            });

        });

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

            // Load correct content and call correct ajax request...

        });

    },

    localLoad: function ( url ) {

        $.ajax({
            url: url,
            success: function (responseHtml) {
                $('.localContent').html($(responseHtml).find('#localHtml'));

                $data = { $(responseHtml).find('#localHtml'), 'local'};

                History.pushState($data, $(responseHtml).filter('title').text(), url);
            }
        });

    },

    globalLoad: function ( url ) {

        $.ajax({
            url: url,
            success: function (responseHtml) {
                $('.mainContent').html($(responseHtml).find('#globalHtml'));

                $data = { $(responseHtml).find('#globalHtml'), 'global'};

                History.pushState($data, $(responseHtml).filter('title').text(), url);
            }
        });

    }

};

App.init();

更新:为了澄清这个问题,我需要帮助解决两个问题。

1.) 使ajax请求的后退和前进按钮正常工作,以便它们将正确的数据加载回内容区域。

2.) 使用pushState方法将内容传递到正确的区域,以便它知道是全局div还是本地div请求。

2个回答

12

数据对象是错误的,这就是为什么你会遇到这个问题。一个对象由键值对定义:

object = {key:'value'};
$data = { text:$(responseHtml).find('#globalHtml'), type:'global', url:'the_url'};

$data = { $(responseHtml).find('#globalHtml'), 'global'};

使用对象定义,您可以使用 History.getState() 获取数据

History.Adapter.bind(window,'statechange',function(){
    var data = History.getState().data;
    if(data){
        var html = data.text;
        var type = data.type;
        var type = data.url;
        if(html && type){
            if(type == 'global'){
                // just change html
                $('.mainContent').html(html);
                // call the App object's function
                if(url) App.localLoad(url);
            }else{
                $('.localContent').html(html);
            }
        }
    }
})

注意:

  • 当状态改变事件被触发时,如果有数据存在,它将触发 ajax 调用
  • 如果要模拟 ajax 调用而不进行实际调用,只需更改标题即可。我认为这样就可以了。

编辑

  • 数据对象实际上来自于 History.getState().data 而不是 History.getState()
  • 添加了另一个参数到存储的对象以保留 url
  • 使用 url 和类型,您可以调用与 onClick 事件中相同的函数

问题:

未捕获的 TypeError: 将循环结构转换为 JSON

一个对象在某个地方引用了自身;因此出现了“循环结构”的消息。这意味着您正在尝试序列化一个具有对其自身的引用的对象,这通常发生在 DOM 元素中。您应该真正考虑更改。

$data = { $(responseHtml).find('#localHtml'), 'local'};

发送给:

$data = { html:$(responseHtml).find('#localHtml').html(), type:'local'};

请注意对象的键(html和type)以及html函数的调用(.html()),这样您就不会发送不必要的数据,只发送HTML。如果您需要在将其作为DOM对象加载时使用HTML数据,请使用以下代码:

var DOM_Element_loaded = $('<div>').html(html_loaded);

现在你可以使用DOM_Element_loaded在加载的HTML中搜索/操作数据,而无需将其附加到body上。


那么我该如何启动它呢?您没有提到popstate吗?因为我需要在用户使用后退和前进按钮时更改内容和标题(实际上复制ajax调用所做的完全相同),但是不使用ajax,而是使用历史数据,因为重新加载显然没有意义。 - Cameron
我也遇到了错误:Uncaught TypeError: Converting circular structure to JSON 有什么想法吗?http://dev.driz.co.uk/ajax/ - Cameron
修改了我的回复,关于JSON错误,请确保传递的是简单对象,没有函数。 - cuzzea
最后一个问题。当您执行pushState时,statechange事件会发生,因此您实际上会进行两次ajax调用或内容更改!一次在单击事件中,然后再次更改状态。是否可能将其分开?因为显然这会引起问题。谢谢。 - Cameron
我正在考虑为主 div 添加一个属性 "page-title='title'",并检查该属性是否与当前标题匹配,如果是,则不加载它。 - cuzzea

0

注意:这实际上更像是一条评论而不是一个答案,但使用答案响应可以获得更好的语法高亮。

当我遇到这个问题时,这就是我所做的。如果我没记错,这可以防止双重请求的问题。

window.onpopstate = function(event) {
  if (event.state && event.state.initialHref) {
    // make an AJAX request
    // URL for AJAX request is event.state.initialHref
  }
}

当我使用history.pushState() / history.replaceState()时,我在对象的第一个参数中定义了一个initialHref属性。我使用的是绝对URL而不是相对URL。这与浏览器历史记录有关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接