おおまえです。ふたたび 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.createContextualFragment
と Range.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'); }
実際にこのコードそのものを使っているわけではないので、何か抜けがあるような気がします。