読者です 読者をやめる 読者になる 読者になる

ExtJS EditorGridPanel でランタイムエラーが発生する問題の回避策 - IE 使用時

JavaScript

ExtJS 2.2.1 の EditorGridPanel を IE6, 7 上で使用している場合、以下のような操作でランタイムエラーが発生する。(ただし、環境や設定によってはランタイムエラーが表示されない可能性あり)

  • 編集中の任意のセルから同じ行の Grid に全体が表示されていない別のセルをクリック

ランタイムエラー発生サンプル

<html>
<head>
<link rel="stylesheet" type="text/css" href="../ext-2.2.1/resources/css/ext-all.css" />
<script type="text/javascript" src="../ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../ext-2.2.1/ext-all.js"></script>
<script type="text/javascript">
    Ext.BLANK_IMAGE_URL = "../ext-2.2.1/resources/images/default/s.gif";

    Ext.onReady(function() {
        var store = new Ext.data.SimpleStore({
            fields: [
                {name: 'title'},
                {name: 'point', type: 'float'}
            ]
        });

        store.loadData([
            ['test1', 1000.10],
            ['a', 50.1]
        ]);

        //グリッドの定義
        var grid = new Ext.grid.EditorGridPanel({
            columns: [
	    		{header: 'Title', dataIndex: 'title', width: 270, editor: new Ext.form.TextField()},
	    		{header: 'Point', dataIndex: 'point', width: 100, editor: new Ext.form.TextField()}
			],
            store: store,
            width: 300,
            height: 100,
            stripeRows: true
        });

        grid.render('grid-sample');
    });
</script>
</head>
<body>
<h1>EditorGridPanel RuntimeError Sample for IE</h1>
<div id="grid-sample"></div>
</body>
</html>

上記ファイルを IE7 等で表示し、Title 列のセルを編集した状態で、右隣の Point 列をクリックするとランタイムエラーが発生。

ランタイムエラーの発生原因

ランタイムエラーの発生原因は getBoundingClientRect メソッドの実行にある。
問題となる操作を実施すると、ExtJS のライブラリ内で getBoundingClientRect を呼び出し、ランタイムエラーが発生する。(IE の場合のみ)

なお、getBoundingClientRect は Firefox 3 でも導入されているが、Firefox では全く問題無い模様。

回避策

この問題を回避するには、getBoundingClientRect の呼び出しを try-catch するだけでよい。


ただし、ExtJS のソースに手を加えるのは望ましい方法では無いため、今回は getBoundingClientRect をラッパーメソッドで置き換える方法を取る事にした。

なお、問題が発生する際の getBoundingClientRect の実行対象は Ext.getDom() で取得していたため、Ext.getDom() で取得したものだけに getBoundingClientRect の置き換えを行うようにしてみた。

<html>
<head>
<link rel="stylesheet" type="text/css" href="../ext-2.2.1/resources/css/ext-all.css" />
<script type="text/javascript" src="../ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../ext-2.2.1/ext-all.js"></script>
<script type="text/javascript">
    Ext.BLANK_IMAGE_URL = "../ext-2.2.1/resources/images/default/s.gif";

    Ext.onReady(function() {
        if (Ext.getDom && !Ext.original_getDom) {
            Ext.original_getDom = Ext.getDom;
            Ext.getDom = function(el) {
                var d = Ext.original_getDom(el);

                if (d && d.getBoundingClientRect && !d.original_getBoundingClientRect) {
                    //オリジナルの getBoundingClientRect を退避
                    d.original_getBoundingClientRect = d.getBoundingClientRect;

                    //getBoundingClientRect をラッパーメソッドで置換
                    d.getBoundingClientRect = function() {
                        var result = {top: 0, left: 0, bottom: 0, right: 0};

                        try {
                            //オリジナルの getBoundingClientRect を実行し
                            //ランタイムエラーを try-catch する
                            result = d.original_getBoundingClientRect();
                        }
                        catch(e) {
                        }
                        return result;
                    };
                }
                return d;
            };
        }
        ・・・
        grid.render('grid-sample');
    });
</script>
</head>
<body>
<h1>EditorGridPanel RuntimeError Sample for IE</h1>
<div id="grid-sample"></div>
</body>
</html>