我遇到Ext.History实用程序(版本3.4.0)在IE8 +中正常工作的问题。它在Quirks模式下工作,但在IE8标准模式(IE8)或IE9标准模式(IE9)下不适用于文档模式。 Quirks模式对我们不起作用,因为它没有正确渲染CSS。
除了历史记录工具之外,我已经删除了应用程序中的所有内容,现在有两个文件(除了extjs文件):
的index.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script type="text/javascript" src="ext-base.js"></script>
<script type="text/javascript" src="ext-all.js"></script>
<script type="text/javascript" src="myapp.js"></script>
</head>
<body>
<div>
<div align="center">
<table width="97%" border="0" cellpadding="0" cellspacing="0" >
<tr>
<td>
<a href="#link1">Link1</a> |
<a href="#link2">Link2</a> |
<a href="#link3">Link3</a> |
<a href="#link4">Link4</a> |
<a href="#link5">link5</a>
</td>
</tr>
</table>
</div>
</div>
<!-- Fields required for history management -->
<form id="history-form" class="x-hidden">
<input type="hidden" id="x-history-field"/>
<iframe id="x-history-frame"></iframe>
</form>
</body>
</html>
myapp.js:
Ext.ns('MyApp');
Ext.onReady(function()
{
Ext.History.init();
Ext.History.on('change', function(token){}, this);
});
当我在Web服务器中加载应用程序时,转到index.html,然后单击link1,地址栏显示#link1。然后我点击link2,地址栏显示#link2。然后我点击link3,地址栏显示#link3。
使用IE中的后退按钮和IE7 Emulation,在Chrome或Firefox中,地址栏将从#link3转到#link2。当我第二次点击后退按钮时,地址栏从#link2转到#link1。这是我期望的行为。
但是,在适当的文档标准模式下使用IE8或IE 9,当我再次单击后退按钮时,地址栏从#link2返回到#link3。进一步点击后退按钮只会在#link2和#link3之间切换用户。此行为是意外的,并导致我们的应用程序无法正常工作。
请注意,这是Sencha示例适用于3.4.0的方式:
(该页面以Quirks模式呈现,但如果您将其更改为IE8标准或IE9标准,则无效)。
它似乎在4.1中正常工作:
(只允许我发布2个链接,但你可以找到它......)
我无法访问Ext 3.4.1,但此问题未在错误修复中列出。我已经看到一个线程(here)表明更改doctype会起作用,但事实似乎并非如此(我已经尝试了所有文档类型......)。
请注意,我们的应用程序的许多部分都使用历史记录实用程序进行导航,因此删除它不是一种可接受的解决方案。
任何人都可以就如何让它发挥作用提出任何建议吗?
答案 0 :(得分:1)
这实际上非常简单。我下载了Ext 4.1并查看了他们使用Ext.util.History类所做的事情。它们为oldIEMode定义一个变量,并将其用于3.4中使用Ext.isIE的所有条件。
所以我在ext-all-debug.js中编辑了Ext.History类,并在顶部定义了以下变量:
var oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict&amp;&amp; Ext.isIE8;
类中有三个条件检查Ext.isIE,我用oldIEMode替换了它。
我重建并部署了应用程序,问题得到解决。
编辑ext-all.js不是最佳做法,但我应该能够覆盖此类。
答案 1 :(得分:1)
这就是我最终解决问题的方法:
我创建了一个新的补丁javascript文件,并将其包含在ext文件
之后/*
@Author: RWR 20130224
This fixes the issue with backward traversal of history in IE8 & higher in standard document mode.
This class was challenging to override (http://www.sencha.com/forum/showthread.php?46306-Help-How-to-extend-Ext.History)
I ended up pasting all of the source original code here and making the necessary changes.
NOTE that this may be patched in version 3.4.1. It is definitely patched in 4.1. When upgrading, validate that this patch is still required.
*/
NewHistory = (function () {
var iframe, hiddenField;
var ready = false;
var currentToken;
var oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8;
function getHash() {
var href = location.href, i = href.indexOf("#"),
hash = i >= 0 ? href.substr(i + 1) : null;
if (Ext.isGecko) {
hash = decodeURIComponent(hash);
}
return hash;
}
function doSave() {
hiddenField.value = currentToken;
}
function handleStateChange(token) {
currentToken = token;
Ext.History.fireEvent('change', token);
}
function updateIFrame (token) {
var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');
try {
var doc = iframe.contentWindow.document;
doc.open();
doc.write(html);
doc.close();
return true;
} catch (e) {
return false;
}
}
function checkIFrame() {
if (!iframe.contentWindow || !iframe.contentWindow.document) {
setTimeout(checkIFrame, 10);
return;
}
var doc = iframe.contentWindow.document;
var elem = doc.getElementById("state");
var token = elem ? elem.innerText : null;
var hash = getHash();
setInterval(function () {
doc = iframe.contentWindow.document;
elem = doc.getElementById("state");
var newtoken = elem ? elem.innerText : null;
var newHash = getHash();
if (newtoken !== token) {
token = newtoken;
handleStateChange(token);
location.hash = token;
hash = token;
doSave();
} else if (newHash !== hash) {
hash = newHash;
updateIFrame(newHash);
}
}, 50);
ready = true;
Ext.History.fireEvent('ready', Ext.History);
}
function startUp() {
currentToken = hiddenField.value ? hiddenField.value : getHash();
if (oldIEMode) {
checkIFrame();
} else {
var hash = getHash();
setInterval(function () {
var newHash = getHash();
if (newHash !== hash) {
hash = newHash;
handleStateChange(hash);
doSave();
}
}, 50);
ready = true;
Ext.History.fireEvent('ready', Ext.History);
}
}
return {
/**
* The id of the hidden field required for storing the current history token.
* @type String
* @property s
*/
fieldId: 'x-history-field',
/**
* The id of the iframe required by IE to manage the history stack.
* @type String
* @property s
*/
iframeId: 'x-history-frame',
events:{},
/**
* Initialize the global History instance.
* @param {Boolean} onReady (optional) A callback function that will be called once the history
* component is fully initialized.
* @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
*/
init: function (onReady, scope) {
if(ready) {
Ext.callback(onReady, scope, [this]);
return;
}
if(!Ext.isReady){
Ext.onReady(function(){
Ext.History.init(onReady, scope);
});
return;
}
hiddenField = Ext.getDom(Ext.History.fieldId);
if (oldIEMode) {
iframe = Ext.getDom(Ext.History.iframeId);
}
this.addEvents(
/**
* @event ready
* Fires when the Ext.History singleton has been initialized and is ready for use.
* @param {Ext.History} The Ext.History singleton.
*/
'ready',
/**
* @event change
* Fires when navigation back or forwards within the local page's history occurs.
* @param {String} token An identifier associated with the page state at that point in its history.
*/
'change'
);
if(onReady){
this.on('ready', onReady, scope, {single:true});
}
startUp();
},
/**
* Add a new token to the history stack. This can be any arbitrary value, although it would
* commonly be the concatenation of a component id and another id marking the specifc history
* state of that component. Example usage:
* <pre><code>
// Handle tab changes on a TabPanel
tabPanel.on('tabchange', function(tabPanel, tab){
Ext.History.add(tabPanel.id + ':' + tab.id);
});
</code></pre>
* @param {String} token The value that defines a particular application-specific history state
* @param {Boolean} preventDuplicates When true, if the passed token matches the current token
* it will not save a new history step. Set to false if the same state can be saved more than once
* at the same history stack location (defaults to true).
*/
add: function (token, preventDup) {
if(preventDup !== false){
if(this.getToken() == token){
return true;
}
}
if (oldIEMode) {
return updateIFrame(token);
} else {
location.hash = token;
return true;
}
},
/**
* Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
*/
back: function(){
history.go(-1);
},
/**
* Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
*/
forward: function(){
history.go(1);
},
/**
* Retrieves the currently-active history token.
* @return {String} The token
*/
getToken: function() {
return ready ? currentToken : getHash();
}
};
})();
Ext.apply(NewHistory, new Ext.util.Observable());
Ext.apply(Ext.History, NewHistory);