JavaScript での IE11 判定を User-Agent を見ずに行う

おおまえです。ふたたび IE11 の話です。タイトルの結論は一番最後にあります。

Internet Explorer 11 がリリースされてしばらく経ち、少しずつ IE11 からアクセスされるようになってきました。Compatibility changes in IE11 (Windows) を見ると User-Agent 文字列の変更はともかくとして IE 独自の API を排除し、いまどきのブラウザが持つような標準に沿った API をちゃんと実装してきている…ようです。

モダンブラウザ用のコードでそのまま動くのを期待していいよね…? と思いたいところですが TracWysiwygPlugin で実際に試してみると、他のブラウザには普通ある API がなくてこの機能が動かないよ…? とかあるわけです。TracWysiwygPlugin IE11 対応を行った際に気づいた点を上げてみました。

1. document.execCommmand(‘inserthtml’) が使えない

WYSIWYG エディタ機能を持つライブラリは、この document.execCommand を用いて色々な文字装飾や任意の HTML を追加したりします。ボールドや斜体などは定義済みのコマンドがあり、第1引数に "bold", "italic" を渡すと選択文字列がコマンドに応じた処理を行ってくれます。また、定義済みのコマンドにないような機能は基本的に "inserthtml" コマンドを用いて必要な HTML を追加します。

しかし IE にはこの inserthtml がありません。IE11 でもありません。他のブラウザにはあるんですが…。また、よくないことに処理しできないコマンドを document.execCommand に渡しても例外などは上げてこないため「エラーになったから代替用のメソッドを…」みたいなことができません。

2. document.selection.createRange ができない

inserthtml の代替手段が document.selection.createRange から TextRange を作成し pasteHTML を呼び出す方法です。これにより inserthtml とほぼ同等のことが実現できます。こんなふうになっています。

TracWysiwyg.prototype._msieInsertHTML = function(html) {
    this.contentWindow.focus();
    var selection = this.contentDocument.selection;
    var range = selection.createRange();
    range.pasteHTML(html.replace(/\t/g, "	"));
    range.collapse(false);
    range.select();
};

しかし IE11 では document.selection がなくなり window.getSelection を使えとあるのですが、このオブジェクトには pasteHTML メソッドがありません。

仕方がないので、以下のようにして Range.createContextualFragmentRange.insertNode を用いて実装するようにしています。もともとこのコードは inserthtml を持たない Safari 2 のために書いたコードです。(消さなくてよかった…)

TracWysiwyg.prototype._fragmentInsertHTML = function(html) {
    var range = this.getNativeSelectionRange();
    if (range) {
        var d = this.contentDocument;
        var tmp = d.createRange();
        tmp.setStart(d.body, 0);
        tmp.setEnd(d.body, 0);
        var fragment = tmp.createContextualFragment(html);
        range.deleteContents();
        range.insertNode(fragment);
        range.detach();
        tmp.detach();
    }
};

3. Selection.containsNode がない

このメソッドは選択領域が指定の要素を内包しているかどうかの判定をしてくれるメソッドです。これが他のブラウザにはあるんですが IE11 にはありません。仕方ないので、これは Range.compareBoundaryPoints を使って、Range オブジェクトと対象の要素の前後関係を順番に調べるというような処理に書き換えました。


ということで、ここまでを踏まえれば User-Agent を見ないで「IE11 判定できそう」と思ったので書いてみました。

    if (!window.TextRange) {
         alert('non IE browser');
    }
    else if (!window.getSelection) {
        alert('IE 8 or early');
    }
    else if (!document.selection) {
        alert('IE 11');
    }
    else {
        alert('IE 9 or 10');
    }

実際にこのコードそのものを使っているわけではないので、何か抜けがあるような気がします。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です