DOM

【JavaScript】 inputまたはtextarea内のテキスト選択を制御する方法

更新日:2023/02/06

ブラウザのtextarea要素でテキストが選択されたとき、何らかの処理をおこないたいケースが時々あります。
今回は、その方法についてお伝えします。

 

テキスト選択するには

inputとtextareaには、自信が保持するテキストを選択するためのメソッドとプロパティが定義されています。

名前内容
メソッドselect()要素内のテキストを全て選択する
setSelectionRange()要素内のテキストを範囲選択する

キャレットを移動する

setRangeText()要素内のテキストを別テキストで置き換える
プロパティselectionStart選択開始位置をセットする
selectionEnd選択終了位置をセットする
selectionDirection選択方向をセットする

これらの機能について、次のhtml要素を使って解説していきます。


<textarea id="textarea" style="width:500px;height:50px;"  autocomplete="off">JavaScript勉強中!プログラマーになりたい!がんばるぞ!
</textarea>

テキストを選択:select()

select()は、要素内のテキストを全て選択するメソッドです。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.select();
});

実行結果は次のようになります。

select()の結果

テキストを範囲選択:setSelectionRange()

setSelectionRange()は、要素内のテキストを範囲選択するメソッドです。

構文

要素.setSelectionRange( start, end , direction )

■引数
start : 選択開始キャレット位置(0~)
end : 選択終了キャレット位置(0~)
direction : 省略可能

startendを同じ値にすることで、選択せずにキャレットを移動できます。

directionは、"forward"(前方から後方に向かって選択)、 "backward"(後方から前方に向かって選択)、 "none"のどれかを指定します。
これにより選択後のキャレット位置を選択範囲の前方/後方のどちらかに移動できます。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.focus();
    tarea.setSelectionRange(1,30);
});

setSelectionRange()の結果を視覚的に表示するには、要素がフォーカスされている必要があります。
フォーカスされていない場合、内部的に開始位置と終了位置がセットされていますが、ブラウザ上で選択表示されません。

実行結果は次のようになります。

setSelectionRange()の結果

このメソッドで変更する値はselectionStartselectionEndselectionDirectionでも設定できます。

選択範囲を置き換える:setRangeText()

setRangeText()は現在選択中または指定した範囲のテキストを、異なるテキストで置き換えます。

構文

要素.setRangeText( replacement , start, end , selectionMode )

■引数

replacement : 置き換え後の文字
start : 省略可能。置き換え範囲の開始キャレット位置
end : 省略可能。置き換え範囲の終了キャレット位置
selectionMode : 省略可能。

現在の選択位置が削除されます。

startendが指定されていない場合、現在の選択位置がstartendとして扱われます。
startendの範囲が削除された後、replacementが挿入されます。
startendが同じ場合、テキストが削除されずにreplacementが挿入されます。

selectionModeは、次の値を指定します。
"select":挿入したテキストが選択されます。
"start":キャレットを挿入したテキストの前方に移動します。
"end":キャレットを挿入したテキストの後方に移動します。
"preserve":規定値。直前の選択をできるかぎり維持します。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.focus();
    tarea.setSelectionRange(4,7);
    tarea.setRangeText("まじで",14 ,26);
});

実行結果は次のようになります。

setRangeText()実行結果

選択開始位置を変更:selectionStart

selectionStartの値を変更すると、選択開始位置を変更できます。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.select();
    tarea.selectionStart = 10;
});

実行結果は次のようになります。

selectionStartの変更結果

選択終了位置を変更:selectionEnd

selectionEndの値を変更すると、選択終了位置を変更できます。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.select();
    tarea.selectionEnd = 10;
});

実行結果は次のようになります。

selectionEndの変更結果

選択方向を変更:selectionDirection

selectionDirectionの値を変更すると、選択方向を変更できます。
指定できる値は "forward",、"backward"、"none"のどれかです。

.


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    tarea.select();
    tarea.selectionDirection = "backward";
});

表示上の変化がないので、実行結果は掲載しません。

 

選択中のテキスト取得

textareaで選択中のテキストを取得するには、selectionStartとselectionEndの値を利用します。

簡単な取得例

次のコードは、ボタンが押されたときにselectionStartとselectionEndの値を確認して、同値なら非選択、異なるなら選択状態とみなしています。そして選択状態なら、textarea内の文字列からsubstringメソッドで文字を切り出しています。

html

<textarea id="textarea" style="width:500px;height:50px;" autocomplete="off">JavaScript勉強中!プログラマーになりたい!がんばるぞ!</textarea>
<button id="button">取得</button>

JavaScript

window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");

    document.getElementById("button").addEventListener("click",()=>{
        const {selectionStart,selectionEnd} = tarea;
        const selectText = selectionStart === selectionEnd ? "選択されていません" :
            tarea.value.substring(selectionStart,selectionEnd);
        alert( selectText );
    });

});

ブラウザによる制限

確認ブラウザ:
Firefox 96.0.1
Google Chrome 97.0.4692.71

Chromeのtextareaはテキスト選択中に、選択外のテキストをCtrlキーを押しながらクリックすると選択が解除されます。
一方Firefoxは選択が解除されずに、下図のように複数選択できます。

Firefoxのtextareaでの複数選択

この状態でコピー&ペーストをおこなうと選択中のテキストが連結されて出力されます。
しかしselectionStartとselectionEndは後から選択した範囲となっているため、プログラムコード上では全ての選択範囲を把握できません。

 

テキスト選択を検知する

textareaでテキスト選択を検知するには、selectイベントを使用します。

selectイベントの簡単な例

次のコードは、マウスクリックで選択範囲をクリアしてもselectイベントが発生しないので、clickイベントを併用しています。

HTML

<textarea id="textarea" style="width:500px;height:50px;" autocomplete="off">JavaScript勉強中!プログラマーになりたい!がんばるぞ!</textarea>
<p id="start"></p>
<p id="end"></p>
<p id="text"></p>

JavaScript


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    const {start,end,text} = ["start","end","text"].reduce((a,b)=>{
        a[b]=document.getElementById(b);
        return a;
    },{});

    const selectFunc = ()=>{
        const {selectionStart,selectionEnd} = tarea;
        start.innerText = selectionStart;
        end.innerText = selectionEnd;
        text.innerText = selectionStart === selectionEnd ? "" : tarea.value.substring(selectionStart,selectionEnd);
    };
    tarea.addEventListener("select",selectFunc);
    tarea.addEventListener("click",selectFunc);
});

selectionchangeイベントでの検知

Selection APIのselectionchangeイベントでもtextareaでの選択を検知できます。
ただし、ブラウザ毎の差異があります。

確認ブラウザ:
Firefox 96.0.1
Google Chrome 97.0.4692.71

Firefoxはtextarea要素でselectionchangeイベントを捕捉できますが、Chromeはできません。
また、FirefoxはSelectionオブジェクトに情報がセットされないため、Selectionオブジェクトから選択文字列を取得できません。


tarea.addEventListener("selectionchange",()=>{
        console.log(window.getSelection().toString() );
            // Firefox:<empty string>
            // Chromeはこのイベントは補足されません
});
document.addEventListener("selectionchange",()=>{
        console.log(window.getSelection().toString() );
            // Firefox:<empty string>
            // Chrome:選択されているテキスト
});

以上を考慮して、FirefoxとChromeの両方に対応させると次のようになります。


window.addEventListener( "DOMContentLoaded" , ()=> {
    const tarea = document.getElementById("textarea");
    const {start,end,text} = ["start","end","text"].reduce((a,b)=>{
        a[b]=document.getElementById(b);
        return a;
    },{});

    const selectFunc = ()=>{
        const {selectionStart,selectionEnd} = tarea;

        start.innerText = selectionStart;
        end.innerText = selectionEnd;
        text.innerText = selectionStart === selectionEnd ? "" : tarea.value.substring(selectionStart,selectionEnd);
    };

    tarea.addEventListener("selectionchange", selectFunc);
    tarea.addEventListener("click",selectFunc);
    document.addEventListener("selectionchange",()=>{
        const selObj = window.getSelection();
        if( selObj.rangeCount === 1 && selObj.getRangeAt(0).comparePoint(tarea,0) ){
            selectFunc();
        }

    });
});

documentのselectionchangeイベントのリスナーでは、Selectionオブジェクト内にtextareaが含まれているかチェックしています。

今後ブラウザの仕様変更があると意味のないコードになるかもしれません。
textaareaの選択変更を検知するならselectionchangeイベントではなくて、selectイベントを使用したほうがよさそうです。

更新日:2023/02/06

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。