LoginSignup
4
4

More than 1 year has passed since last update.

【JavaScript】Selectionについて図解で理解する。

Posted at

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のように、複数選択できるブラウザの場合に、どの選択範囲かを指定するためです。

b4e055fc1e9401b9dde8a5cf6306f4d6.gif
chromeの場合、複数選択することはできませんので、実質的に selection.getRangeAt(0) で取得する形となります。

下記図のように、選択範囲を削除する実装を行ってみます。

8898c343ac809359102b457638887211.gif  

<!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"を使用しましたが、選択範囲自体によるイベントハンドラも存在します。

  1. selectstart : ユーザがボタンを押しながらマウスを動かし始めた時など、選択が開始された時に発火。
  2. 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の範囲が選択された状態となります。
image.png

まずは、下記で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と類似のプロパティーを持っています。

  1. anchorNode : selectionの開始ノードです。
  2. anchorOffset : anchorNodeでの開始点となるオフセットです。
  3. focusNode : selectionの終了ノードです。
  4. focusOffset : focusNodeでの終了点となるオフセットです。
  5. isCollapsed : selectionが未選択(空の範囲) あるいは存在しない場合 true になります。
  6. 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が手前に来ることがあります。
20cb0d069f23ddc3e810f33842ea9530.gif
上記の様に、逆から選択してみます。

<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となります。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4