背靠背和pushState——一个关于两段历史的故事
.back, and .pushState - a tale of two histories
与其说这是一个问题,不如说这是对一个有趣问题的发现。这也是一种"从我的失败中学习"
我正在尝试为IE编写HTML5历史鸭子穿孔的单元测试(使用window.hash代替状态维护)。duckpunch按预期工作,在用户测试期间,我在IE、Chrome和Firefox中得到了一致的结果。
问题出在单元测试上。在它们中,我对history.pushState()、.replaceState、.back()和.forward()进行了各种组合。这些在Firefox和IE中运行良好,但Chrome给了我完全不一致的结果。下面的答案解释了原因。
考虑以下内容:
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 duck punch中,这正是它所做的——但在Chrome中,它的响应是"Baz"。
经过一番调查,我解决了这个问题:IE和Firefox同步更新历史记录,如果需要加载任何页面,则异步更新。Chrome似乎立即异步运行。
证据:
window.onpopstate = function () {
console.log('ping');
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('pong');
在Firefox中,返回"ping"pong'-表示事件是作为history.back()调用的一部分进行调度的。在Chrome中,返回"pong"ping’—表示事件已放入队列中进行调度。
如果这个事件调度模型没有被用来管理历史和位置对象的状态,这不会那么糟糕——但显然是这样
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());
这是一个有趣的怪癖,需要使用jQueryDeferred链来正确处理我的单元测试。我对此不是特别高兴,但你能做什么?
为了处理单元测试中的异步返回事件,我使用了HistoryJS和Jasmine。这是一个更新历史事件中的计数器以跟踪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
});
}
}
- Angular Form Wide验证(不特定于字段)
- 一个具有两个图像按钮的表单在只能按下一个按钮的情况下发送两个按钮值
- 如果使用jQuery验证器插件找到单选按钮,则将不同的addMethod应用于字段
- 如何将文本从一个文本字段复制到其他文本字段
- 单击按钮时如何聚焦下一个文本字段
- 创建一个HTML文件,该文件使用DOM在用户每次单击按钮时交换两段内容
- jQuery将输入集中在下一个tr->td时,td包含一个输入字段,但从不关注html选择
- 查找与锁定和更新调度相关的一个或两个节点模块
- Regex适用于所有段集管(2.0、2.1.2、2.3.1.5等)
- 背靠背和pushState——一个关于两段历史的故事
- 一个表单字段贯穿两个 DIVS
- 添加混合 javascript 函数,使内容淡入列内的另一个内容/使其适用于两个单独的列
- 如何在HTML中使用相同的输入值拥有一个输入字段,两个按钮和两个操作
- 添加两个文本字段值,并使用 Javascript 中的 OnChange 在另一个文本字段中显示结果
- target_blank适用于两个javascript函数,即使它适用于一个javascript函数
- 我在html中有两个输入字段,我在想如何将第一个输入字段的值自动转换为下一个输入字段
- 创建一个选择字段,其中的时间条目不能早于当前计算机时间
- 启用依赖于另一个表单字段的值的表单字段
- 使用knockout.js将两个值绑定到一个输入字段
- 在js表单上,如何隐藏/显示单选按钮(一组3个按钮中的一个或两个)取决于在上面字段中输入的数字的值