JavaScript Rangeの使い方
Rangeオブジェクトに関するメモ。
[関連仕様]
IE
Mozilla
分断された領域が複数選択されている場合は、Selectionは複数のRangeを持つ(以下参照)。
このような選択はIEやWindows版FireFoxではできなかった。この他に複数のRangeを持つ選択方法にどのようなものがあるかは不明。
Rangeの数はrangeCountプロパティで取得可能。
DOM仕様書にこれらのプロパティについて図解入りで説明がありわかりやすい。
文字を選択した時に表1のプロパティがどのように変化するかを表示するサンプル。
http://www.bit-hive.com/~tomita/RangeDump/
JavaScriptのソースはhttp://www.bit-hive.com/~tomita/RangeDump/range.js
3. 現在の選択範囲の取得の仕方(IEの場合)
IEのRangeオブジェクトにはテキストの選択範囲を表すTextRangeと画像などのコントロールオブジェクトの選択を表すControlRangeの2種類が存在する。
3.1 TextRange,ControlRangeの取得
IEの場合Selectionはdocument内に既にあるので以下のようにして取得できる。
現在選択しているのがテキストであればTextRangeが返され、コントロールであればControlRangeの集まりであるControlRangeCollectionが返される。現在の選択範囲のタイプはdocument.selection.typeプロパティによって判定できる。選択範囲がなければ"none"、テキストを選択していれば"text"、コントロールを選択していれば"Control"を持つ。この値をチェックすれば、返されたオブジェクトがTextRangeなのか、ControlRangeCollectionなのかを判定できる。
textプロパティで選択範囲の文字列を取得したり、pasteHTML()メソッドで選択範囲にHTMLを貼り付けたりできる。
4. サンプルコード その1
実際にRangeオブジェクトを使ったサンプル。
このサンプルはIFRAME内の選択領域にHTMLオブジェクトを挿入する。
IEではcreateRange()でRangeオブジェクトを取ってしまえば、pasteHTML()で張りつけることができる。Mozilla系では選択範囲のDOMオブジェクトを操作する必要がある。
実働サンプル:
http://www.bit-hive.com/~tomita/RangeInsert/
JavaScriptのソース:
http://www.bit-hive.com/~tomita/RangeInsert/insert.js
囲むタグはencap_node引数で指定する。encap_nodeは document.createElement("p") のようにして作成されたDOM Nodeを指定する。
Mozilla系ではsurroundContents()メソッドで一発でできるが、IEでは指定タグでカプセル化したHTMLを作成してpasteHTML()で選択範囲のHTMLを入れ換えてやる。
実働サンプル:
http://www.bit-hive.com/~tomita/RangeEncap/
JavaScriptのソース:
http://www.bit-hive.com/~tomita/RangeEncap/encap.js
[関連仕様]
IE
Mozilla
1.SelectionとRangeの関係
Selectionはカーソルの選択領域を示す。Rangeは1つの連続した選択領域を表しSelectionに格納されている。通常はSelectionの中にはRangeは1つだけ存在する。分断された領域が複数選択されている場合は、Selectionは複数のRangeを持つ(以下参照)。
Selectionの中に複数のRangeが含まれるケース
Linux版FireFoxだと、designMode = "On"の時に図1のようにセルを選択できる。このような場合、選択した各々のセルが1つのRegionとなり、Selectionに3つのRangeが含まれることになる。このような選択はIEやWindows版FireFoxではできなかった。この他に複数のRangeを持つ選択方法にどのようなものがあるかは不明。
図1 複数Range存在するケース
2.現在の選択範囲の取得の仕方(Mozilla系)
2.1 Selectionの取得
まずはSelectionを取得する。var selection = window.getSelection();
2.2 Rangeの取得
Selection内の最初のRangeを取得するには以下のようにする。var range = selection.getRangeAt(0);
Rangeの数はrangeCountプロパティで取得可能。
selection.rangeCount;
2.3 Rangeの取り扱い
Rangeから具体的な選択範囲を取得するには表1に示すプロパティを使う。表1 Rangeオブジェクトのよく使うプロパティ
プロパティ | 意味 |
---|---|
startContainer | 選択開始位置のDOM Node |
startOffset | 選択開始位置のstartContainer内でのオフセット |
endContainer | 選択終了位置のDOM Node |
endOffset | 選択終了位置のendContainer内でのオフセット |
文字を選択した時に表1のプロパティがどのように変化するかを表示するサンプル。
http://www.bit-hive.com/~tomita/RangeDump/
JavaScriptのソースはhttp://www.bit-hive.com/~tomita/RangeDump/range.js
3. 現在の選択範囲の取得の仕方(IEの場合)
IEのRangeオブジェクトにはテキストの選択範囲を表すTextRangeと画像などのコントロールオブジェクトの選択を表すControlRangeの2種類が存在する。3.1 TextRange,ControlRangeの取得
IEの場合Selectionはdocument内に既にあるので以下のようにして取得できる。var range = document.selection.createRange();
現在選択しているのがテキストであればTextRangeが返され、コントロールであればControlRangeの集まりであるControlRangeCollectionが返される。現在の選択範囲のタイプはdocument.selection.typeプロパティによって判定できる。選択範囲がなければ"none"、テキストを選択していれば"text"、コントロールを選択していれば"Control"を持つ。この値をチェックすれば、返されたオブジェクトがTextRangeなのか、ControlRangeCollectionなのかを判定できる。
if (document.selection.type != "Control") {
// TextRange
var textrange = document.selection.createRange();
} else {
// ControlRangeCollection
var ctrlcollection = document.selection.createRange();
}
3.2 TextRangeの取り扱い
IEではMozilla系のようにstartContainerなどのプロパティは存在しない。IEのRangeオブジェクトの仕様はこちらを参照。textプロパティで選択範囲の文字列を取得したり、pasteHTML()メソッドで選択範囲にHTMLを貼り付けたりできる。
3.3 ControlRangeCollectionの取り扱い
ControlRangeCollectionは配列のような構造をしており、各要素には選択したDOM Nodeが格納されていて以下のように参照できる。// コレクション内の要素数
ctrlcollection.length
// 0番目の選択したDOM Node
ctrlcollection(0)
// タグ名の参照
ctrlcollection(0).tagName
4. サンプルコード その1
実際にRangeオブジェクトを使ったサンプル。このサンプルはIFRAME内の選択領域にHTMLオブジェクトを挿入する。
IEではcreateRange()でRangeオブジェクトを取ってしまえば、pasteHTML()で張りつけることができる。Mozilla系では選択範囲のDOMオブジェクトを操作する必要がある。
IE用コード
function insertHTMLforIE(html)
{
// iframeのdocument,windowを取得
var win = frames['frame'].window;
var doc = frames['frame'].document;
win.focus();
range = doc.selection.createRange();
try {
range.pasteHTML(html);
} catch (e) {
alert(e);
}
}
Mozilla系用のコード
function insertHTMLForMozilla(html)
{
// iframeのdocumentとwindowを取得
var win = document.getElementById('frame').contentWindow;
var doc = document.getElementById('frame').contentDocument;
var fragment = doc.createDocumentFragment();
var div = doc.createElement("div");
div.innerHTML = html;
// div配下のNodeをfragmentに移動
while (div.firstChild) {
fragment.appendChild(div.firstChild);
}
var selection = win.getSelection();
range = selection.getRangeAt(0);
// 選択範囲の削除
range.deleteContents();
var container = range.startContainer;
var offset = range.startOffset;
switch (container.nodeType) {
case 1:
// Element node
container.insertBefore(fragment,
container.childNodes[offset]);
break;
case 3:
// Text node
var node = container.splitText(offset);
node.parentNode.insertBefore(fragment, node);
break;
}
}
実働サンプル:
http://www.bit-hive.com/~tomita/RangeInsert/
JavaScriptのソース:
http://www.bit-hive.com/~tomita/RangeInsert/insert.js
5. サンプルコード その2
選択範囲を指定したタグで囲む処理を行うコード。囲むタグはencap_node引数で指定する。encap_nodeは document.createElement("p") のようにして作成されたDOM Nodeを指定する。
Mozilla系ではsurroundContents()メソッドで一発でできるが、IEでは指定タグでカプセル化したHTMLを作成してpasteHTML()で選択範囲のHTMLを入れ換えてやる。
IE用コード
function encapHTMLforIE(encap_node)
{
// iframeのwindowを取得
var win = frames['frame'].window;
win.focus();
var range = doc.selection.createRange();
var container = document.createElement("div");
container.appendChild(encap_node);
encap_node.innerHTML = range.htmlText;
try {
range.pasteHTML(container.innerHTML);
} catch (e) {
alert(e);
}
}
Mozilla系用コード
function encapHTMLForMozilla(encap_node)
{
// iframeのdocumentとwindowを取得
var win = document.getElementById('frame').contentWindow;
var doc = document.getElementById('frame').contentDocument;
var selection = win.getSelection();
var range = selection.getRangeAt(0);
range.surroundContents(encap_node);
}
実働サンプル:
http://www.bit-hive.com/~tomita/RangeEncap/
JavaScriptのソース:
http://www.bit-hive.com/~tomita/RangeEncap/encap.js