3

我遇到了 Ext.History 实用程序(版本 3.4.0)在 IE8+ 中正常工作的问题。它适用于 Quirks 模式,但不适用于 IE8 标准模式 (IE8) 或 IE9 标准模式 (IE9) 中的文档模式。Quirks 模式对我们不起作用,因为它不能正确渲染我们的 CSS。

我已经从应用程序中删除了除历史实用程序之外的所有内容,现在有两个文件(除了 extjs 文件):

索引.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。

在带有 IE7 Emulation 的 IE、Chrome 或 Firefox 中使用后退按钮,地址栏将从 #link3 变为 #link2。当我第二次点击返回按钮时,地址栏从#link2 变为#link1。这是我所期望的行为。

但是,在适当的文档标准模式下使用 IE8 或 IE 9,当我再次单击后退按钮时,地址栏会从 #link2 返回到 #link3。进一步点击后退按钮只会在#link2 和#link3 之间切换用户。这种行为是出乎意料的,导致我们的应用程序无法正常工作。

请注意,这是 Sencha 示例适用于 3.4.0 的方式:

Sencha 3.4 示例

(页面以 Quirks 模式呈现,但如果您将其更改为 IE8 标准或 IE9 标准,它将不起作用)。

它确实工作似乎在 4.1 中正常工作:

(只让我发布 2 个链接,但你可能会找到它......)

我无权访问 Ext 3.4.1,但此问题未在错误修复中列出。我看到一个线程(here)表明更改文档类型会起作用,但似乎并非如此(我已经尝试了所有文档类型......)。

请注意,我们应用程序的许多部分都使用历史实用程序进行导航,因此删除它不是一个可接受的解决方案。

任何人都可以就我如何让它发挥作用提供任何建议吗?

4

2 回答 2

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 && Ext.isIE8;

类中有三个条件检查 Ext.isIE,我将其替换为 oldIEMode。

我重建并部署了应用程序,问题得到解决。

编辑 ext-all.js 不是最佳实践,但我应该能够覆盖这个类。

于 2013-02-24T00:51:54.860 回答
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);
于 2013-02-24T13:28:44.463 回答