.back和.pushState - 两个历史的故事

时间:2012-07-11 20:34:07

标签: javascript html5 history

这不是一个问题,而是一个有趣问题的结果。它也是一种“从我的失败中学习”

我正在尝试为IE浏览器编写HTML5历史记录的单元测试(使用window.hash替代状态维护)。 duckpunch按预期工作,在用户测试期间,我在IE,Chrome和Firefox中获得了一致的结果。

问题出在哪里就是单元测试。在其中我执行history.pushState(),. renplaceState,.back()和.forward()的各种组合。这些在Firefox和IE中运行良好,但Chrome给了我彻底不一致的结果。以下答案解释了原因。

2 个答案:

答案 0 :(得分:8)

请考虑以下事项:

var originalPath = window.location.pathname.toString();
history.pushState({ value: 'Foo' }, '', 'state-1');
history.pushState({ value: 'Bar' }, '', 'state-2');

history.pushState({ value: 'Baz' }, '', 'state-3');
history.back();
history.back();
console.log(history.state.value);
//So we can hit refresh and see the results again...
setTimeout(function () {
    history.replaceState(null, '', originalPath);
}, 250);

人们会期望这一大块代码能够返回' Foo' - 在Firefox和我的IE浏览器中,这正是它的作用 - 但在Chrome中,它以“Baz'”作为回应。

经过一番调查后,我解决了问题:IE和Firefox同步更新历史记录,如果需要加载任何页面,则转为异步。 Chrome似乎立即异步。

证据:

window.onpopstate = function () {
    console.log('ping');
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('pong');

在Firefox中,这会返回' ping&#39 ;; '乒乓' - 指示事件是作为history.back()调用的一部分调度的。在Chrome中,这会返回&#p; pong&#39 ;; '平' - 表示事件被放入队列中进行调度。

如果此事件调度模型未用于管理历史记录和位置对象,那么这不会太糟糕。国家 - 但显然是。

window.onpopstate = function () {
    console.log('Event...', history.state, window.location.pathname.toString());
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('Inline...', history.state, window.location.pathname.toString());

这是一个有趣的怪癖,需要使用jQuery Deferred链来正确地进行单元测试。我对此并不特别高兴,但你能做什么?

答案 1 :(得分:2)

为了处理单元测试中的异步后退事件,我使用了HistoryJSJasmine。这是一个更新历史事件中的计数器以跟踪chrome处理事件的时间和Jasmine的异步支持以阻止单元测试直到我们看到事件发生变化的情况:

递增计数器:

History.Adapter.bind(window, 'statechange', function() {

    // Increment the counter
    window.historyEventCounter++;

});

Jasmine异步单元测试。 waitsFor将阻塞,直到历史事件发生:

describe('back navigation', function () {
    it('should change url', function () {

        var initialHistoryEventCount;

        // Cache the initial count

        runs(function() {
            initialHistoryEventCount = window.historyEventCounter;
            History.back();
        });

        // Block until the event counter changes

        waitsFor(function() {
            return (initialHistoryEventCount !== app.historyEventCounter);
        }, "The page should be navigated back", 1000);

        // Now confirm the expected back behaviour. Event failures will time-out.

        runs(function() {
            expect(window.location.href).toEqual("http:// my back url");

            // ... more tests about the page state
        });
    }
}