Javscriptの開発において、1つ大きな障害となるのはクロスブラウザです。その中でも、rangeやselection関連のクロスブラウザ対策には辟易します。
毎度検索エンジンで調べている気がしたので、今回はこれらをまとめてみようと思います。
jQueryの利用を前提としていますが、限定的な利用なので、例えば$("textarea")[0]はdocument.getElementsByTagName("textarea").item(0)などで置き換えらえます。DOM要素を指定している個所は、利用シーンに合わせて変換してご利用ください。
テキストを取得する対象の要素 | createRange可否 | 処理内容 |
---|---|---|
平文(textarea/iframe以外) | 使えるなら(旧IE系)→ | var range = document.selection.createRange(); var txt = range.text; |
使えないなら→ | var range = window.getSelection().getRangeAt(0); var txt = range.toString(); | |
textarea | 使えるなら(旧IE系)→ | var range = document.selection.createRange(); var txt = range.text; |
使えないなら→ | var ta = $("textarea")[0]; var val = ta.value; var start = parseInt(ta.selectionStart, 10); var end = parseInt(ta.selectionEnd, 10); var txt = val.substring(start, end); | |
designMode=onのiframe | 使えるなら(旧IE系)→ | var range = $("iframe")[0].contentWindow.document.selection.createRange(); var txt = range.text; |
使えないなら→ | var range = $("#iframe")[0].contentWindow.getSelection().getRangeAt(0); var txt = range.toString(); |
txtの取得がゴールです。
旧IE系のcreateRangeの便利さが光ります。全て同じやり方で取得できます。
一方、旧IE系以外はtextareaだけが取得方法が大きく異なります。
実際のコードは下記のように分岐してご利用ください。
if(typeof(document.selection) != "undefined") {
var range = document.selection.createRange();
var txt = range.text;
} else if(typeof(window.getSelection) != "undefined") {
var range = window.getSelection().getRangeAt(0);
var txt = range.toString();
}
尚、IE9~10は、getSelectionとcreateRangeの両刀使いのため、注意しないとバグの温床となります。
今回のテーマでは、createRangeが優秀なので、基本的にdocument.selectionの判定を先に行って、createRangeを優先させるのをオススメします。
テキストを取得する対象の要素 | createRange可否 | 処理内容 |
---|---|---|
平文(textarea/iframe以外) | 使えるなら(旧IE系)→ | var range = document.selection.createRange(); var html = range.htmlText; |
使えないなら→ | var range = window.getSelection().getRangeAt(0); var div = $("<div></div>").html(range.cloneContents() ); var html = div.html(); | |
designMode=onのiframe | 使えるなら(旧IE系)→ | var range = $("iframe")[0].contentWindow.document.selection.createRange(); var html = range.htmlText; |
使えないなら→ | var range = $("#iframe")[0].contentWindow.getSelection().getRangeAt(0); var div = $("<div></div>").html(range.cloneContents() ); var html = div.html(); |
変数htmlの取得がゴールです。
HTMLを扱わないtextareaを除外しているため、旧IE系とそれ以外の両方とも似たようなコードになっています。
range内をcloneContentsで一時的なdivに設置してから中身を取得している旧IE系以外に対し、range.htmlTextで一発取得できるcreateRangeのスマートさがここでも光ります。
テキストを取得する対象の要素 | createRange可否 | 処理内容 |
---|---|---|
textarea | 使えるなら(旧IE系)→ | var range = document.selection.createRange(); range.text = txt_replace; |
使えないなら→ | var ta = $("textarea")[0]; var val = ta.value; var start = parseInt(ta.selectionStart, 10); var end = parseInt(ta.selectionEnd, 10); var txt_before = val.substring(0, start); var txt_after = val.substring(end); ta.value = txt_before + txt_replace + txt_after; | |
designMode=onのiframe | 使えるなら(旧IE系)→ | var range = $("iframe")[0].contentWindow.document.selection.createRange(); range.text = txt_replace; |
使えないなら→ | var range = $("iframe")[0].contentWindow.getSelection().getRangeAt(0); $("iframe")[0].contentWindow.document.execCommand("insertHTML", false, txt_replace); |
txt_replaceは置換後の文字を指します。
createRangeが使えない場合、textareaでは、取得した選択始点・選択終点から前後の文字を取得し、すべてを繋ぎ合わせてtextarea内を全て置換するという力技です。一方designModeでは、execCommandを利用しています。
createRangeが使える場合は、1行で済んでいてとてもスマートです。
とにかくcreateRangeが使いやすくて便利です。
しかし、IE独自仕様のため、getSelectionとの併用となったIE9~IE10を経て、IE11では使用ができなくなりました。
本件に関しては、他のブラウザがIEの仕様に歩み寄ってほしかった、というのが正直な感想です。
まめわざでは、WYSIWYGエディターを自社開発して利用しています。
例えば、テーブルのセル1つ1つがWYSIWYGエディターとして動作します。これにより、WYSIWYGエディター内でテーブルを編集した場合とは大きく異なる操作性を実現しています。
WYSIWYGエディターであることを意識させないために、右クリックによってエディターを起動させる方法を採用しています。
また、Enterキーによって段落が分かれる(隙間が空く)従来のWYSIWYGエディターとは異なり、textareaのような操作感を実現しています。
必要であればリンクやテキストの彩色・拡大は可能、という「必要のない人には気付かせず、必要な人には機能を提供する」ことを目指したインターフェースです。
尚、文字の過度な彩色や拡大は、判読性を下げ(読みづらくなり)、デザイン性を損なう可能性があることを常に意識しましょう。