JavaScriptのSelectionについて、よく分からなかったので、図を交えて理解してみました。
Selectionとは
Selectionとは、ざっくり言ってしまうとユーザーが選択した範囲を表すものです。
前回のRangeの説明では、Rangeの範囲をプログラム上で設定していましが、Selectionを使用する事により、ユーザーが選択した範囲をRangeに設定する事ができます。
下記のコードで、ユーザーの選択範囲からRangeオブジェクトを取得することが可能です。
const selection = document.getSelection();
const range = selection.getRangeAt(0);
まず、下記でSelectionオブジェクトを取得します。
const selection = document.getSelection();
次に、下記のコードからselectionオブジェクトの1番目(インデックス0)からRangeオブジェクトを取得しています。
const range = selection.getRangeAt(0);
なぜ、1番目を指定する必要があるのか?
FireFoxのように、複数選択できるブラウザの場合に、どの選択範囲かを指定するためです。
chromeの場合、複数選択することはできませんので、実質的に selection.getRangeAt(0) で取得する形となります。
下記図のように、選択範囲を削除する実装を行ってみます。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="words">
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
</div>
<button onclick="deleteNode()">削除</button>
<script type="text/javascript">
let range;
document.addEventListener('mouseup', function (ev) {
const selection = document.getSelection();
range = selection.getRangeAt(0);
}, false);
function deleteNode() {
range.deleteContents();
}
</script>
</body>
</html>
下記のコードで、マウスボタンが離された時に、Selectionオブジェクトを作成し、Rangeオブジェクトを取得しています。
document.addEventListener('mouseup', function (ev) {
const selection = document.getSelection();
range = selection.getRangeAt(0);
}, false);
次に、ボタンをクリックした場合に、rangeのノードを削除する関数を定義しています。
deleteContentsについて分からない場合は、Rangeの説明をご確認ください。
function deleteNode() {
range.deleteContents();
}
今回はイベントハンドラとして"mouseup"を使用しましたが、選択範囲自体によるイベントハンドラも存在します。
- selectstart : ユーザがボタンを押しながらマウスを動かし始めた時など、選択が開始された時に発火。
- selectionchange : 選択範囲が変更された時(documentに対してのみ設定可能)
addRangeメソッド
逆にrangeオブジェクトからSelectionオブジェクトを設定することも可能です。
addRangeを使用します。
引数には、Rangeオブジェクトを渡します。
<body>
<div id="words">
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
</div>
<button onclick="setSelection()">選択範囲設定</button>
<script type="text/javascript">
const range = new Range();
const words = document.getElementById("words");
range.setStart(words.children[0].childNodes[0], 3)
range.setEnd(words.children[2].childNodes[0], 5)
function setSelection() {
const sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
</script>
</body>
「選択範囲設定」をクリックすると、下記の図のようにrangeの範囲が選択された状態となります。
まずは、下記でrangeで範囲を設定しています。
rangeの範囲の設定について分からない場合は、Rangeの説明をご確認ください。
const range = new Range();
const words = document.getElementById("words");
range.setStart(words.children[0].childNodes[0], 3)
range.setEnd(words.children[2].childNodes[0], 5)
下記関数で選択範囲にrangeを追加しています。
Firefox以外のブラウザは選択範囲がすでに存在する場合、新しいrangeを無視します。
ですので、sel.removeAllRanges() で、範囲を一度空にしています。
sel.addRange(range) でrangeをselectionに追加します。
function setSelection() {
const sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
Selectionのプロパティー
SelectionもRangeと類似のプロパティーを持っています。
- anchorNode : selectionの開始ノードです。
- anchorOffset : anchorNodeでの開始点となるオフセットです。
- focusNode : selectionの終了ノードです。
- focusOffset : focusNodeでの終了点となるオフセットです。
- isCollapsed : selectionが未選択(空の範囲) あるいは存在しない場合 true になります。
- rangeCount : selectionに含まれるRangeオブジェクトの数です。
下記のようにselectionにrangeを設定した場合、当然rangeとselectionの範囲が同じとなるので、以下のログは全てtrueとなります。
function setSelection() {
const sellection = document.getSelection();
sellection.removeAllRanges();
sellection.addRange(range);
console.log(range.startContainer === sellection.anchorNode); //true
console.log(range.startOffset === sellection.anchorOffset); //true
console.log(range.endContainer === sellection.focusNode); //true
console.log(range.endOffset === sellection.focusOffset); //true
}
Rangeオブジェクトの場合、開始点が終了点より手前に来ることはありませんが、selectionの場合はanchorよりfocusが手前に来ることがあります。
上記の様に、逆から選択してみます。
<body>
<div id="words">
<p>ABCDEFGHIJKLMNOPQRSTUVWXYZ</p>
<p>1234567890</p>
<p>あいうえおかきくけこ</p>
</div>
<button onclick="setSelection()">選択範囲設定</button>
<script type="text/javascript">
function setSelection() {
const sel = document.getSelection();
console.log("anchorNode", sel.anchorNode); //"あいうえおかきくけこ"
console.log("focusNode", sel.focusNode); //"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
</script>
</body>
focusNodeがanchorNodeより手間に来ていることがわかります。
anchorが終点focusの前にある場合、この選択は "正" 方向と呼ばれます。
逆の場合は"負"の方向と呼ばれます。
選択範囲を直接設定する。
selectionは、addRangeでrangeを追加せずとも直接範囲を指定することも可能です。
collapseメソッド (エイリアス : setPositionメソッド)
第一引数にノード。第二引数にoffsetの値を取ります。引数のノードのoffsetの位置に開始及び終了点を設定します。
collapseToStartメソッド
現在の選択範囲の開始点に終了点を合わせます。引数はありません。
collapseToEndメソッド
現在の選択範囲の終了点に開始点を合わせます。引数はありません。
extendメソッド
第一引数にノード。第二引数にoffsetの値を取ります。
選択範囲のfocusを引数のノードのoffsetの位置に移動します。
setBaseAndExtentメソッド
第一引数にanchorに設定したいノード。第二引数にoffsetの値、第三引数にfocusに設定したいノード。第四引数にoffsetの値を設定します。
引数に渡されたされた開始点 anchorNode(第一引数)/anchorOffset(第二引数) と終了点 focusNode(第三引数)/focusOffset(第四引数) に設定します。
selectAllChildrenメソッド
引数にノードを設定します。引数に渡されたノードの全ての子ノードを選択範囲に設定します。
deleteFromDocumentメソッド
ドキュメントから選択された範囲を削除します。引数はありません。
containsNodeメソッド
第一引数にノード、第二引数にbooleanを渡します。
選択範囲が第一引数のノードを含むかチェックします。含む場合はtrue、含まない場合はfalseを返します。
第二引数が true の場合は部分的に含む場合も、含むと判断してtrueを返します。
第二引数が false の場合は、ノードが選択範囲に完全に含まれている場合にのみ、trueを返します。
第二引数を省略した場合は、デフォルトでfalseとなります。