テキストボックス内のカーソル(キャレット)位置や選択範囲を,JavaScriptで取得・設定する方法
input や textarea などのテキストボックスをフォーカスした際に表示される,「|」の形をしたカーソルの事をキャレットという。
また,Shiftキーを押しながらキャレットを移動させると,文字列が選択状態になる。
キャレットや選択文字列の情報は,JavaScriptで取得・設定できる。
下記は,IEで・・・
- カーソル(キャレット)の位置を
- 取得する方法。
- 設定する方法。つまり,カーソルの移動。
- 選択状態になった文字列の範囲を
- 取得する方法。
- 設定する方法。つまり,指定した範囲を選択する。
のサンプルコード。
また,これらを応用して
- テキストボックスで,クリックした文字を取得する方法
についても述べる。
なお,Mozilla Firefoxでは下記を使って楽に操作できる。
- 要素.selectionStart = 開始位置;
- 要素.selectionEnd = 終了位置;
- 要素.setSelectionRange( 開始位置, 終了位置 );
Mozillaのサイト:selectionStartプロパティとは
https://developer.mozilla.org/ja/XUL/Property/selectionStart
window.getSelection と document.getSelectionの違い
http://d.hatena.ne.jp/teramako/20080209/p1
もし上と同じことを IE でやろうとすると,日本語圏では情報が極めて手に入りづらい。
(1)IEで,カーソル位置を取得するサンプル
テキストボックス内のカーソルの位置を常にモニタするようなコード。
<input type="text" size=30 id="my_input" value="hogefuga"> <input type="button" onclick="f()" value="キャレットの位置を取得"> <!-- カーソルの位置が,常にここにモニタされます。 --> <div id="d"></div> <script language="JavaScript"> function f() { var elem = document.getElementById( "my_input" ); d.innerHTML = getCaretPositionIE( elem ); } // 要素内のキャレット位置を取得する関数 function getCaretPositionIE( elem ) { elem.focus(); // ボックスの先頭からキャレットまでのrangeを作って,長さを調査 var range = document.selection.createRange(); range.moveStart( "character", - elem.value.length ); var caret_position = range.text.length; return caret_position; } // イベント割り当て my_input.onclick = f; my_input.onkeyup = f; </script>
moveStartには,ボックスの末尾からの負の文字数を指定する。
イベント割り当て部分で,fをkeydownにセットしてしまうと「移動前」の位置が出てしまう。
カーソルの「移動後」を捉えるにはkeyupが必要。
(2)IEで,カーソル位置を設定するサンプル
位置のインデックスが0始まりだとして,ボタンクリック時に5文字目にカーソルをセットしてみる。
<input type="text" size=30 id="my_input" value="hogefuga"> <input type="button" onclick="f()" value="キャレットの位置を設定"> <script language="JavaScript"> function f() { var elem = document.getElementById( "my_input" ); setCaretPositionIE( elem, 5 ); } // 要素内のキャレット位置を設定する関数 function setCaretPositionIE( elem, caret_position ) { var range = elem.createRange(); range.collapse(); // rangeの幅を新規設定 range.moveEnd( "character", caret_position ); // endを先に range.moveStart( "character", caret_position ); range.select(); } </script>
moveEndを先に,moveStartをあとで設定すること。
(3)IEで,選択範囲の取得をするサンプル
選択状態になった文字列の
- 開始位置
- 終了位置
- 内容
をアラート表示。
<input type="text" size=30 id="my_input" value="hogefuga"> <input type="button" onclick="f()" value="選択範囲を調査"> <script language="JavaScript"> function f() { var elem = document.getElementById( "my_input" ); getSelectionIE( elem ); } // 要素内の選択文字列を調査する関数 function getSelectionIE( elem ) { // ページ内で選択中のレンジ var selected_range = document.createTextRange(); if( ! ( selected_range.text.length > 0 ) ) { alert( "選択されていません。" ); return; } // 始点を調査 // ボックスの先頭から,選択範囲の始点までのrangeを作って長さを取得 var range_start = elem.createRange(); range_start.setEndPoint( "EndToStart", selected_range ); var start_point = range_start.text.length; // 終点を調査 // ボックスの先頭から,選択範囲の終点までのrangeを作って長さを取得 var range_end = elem.createRange(); range_end.setEndPoint( "EndToEnd", selected_range ); var end_point = range_end.text.length; alert( start_point + "文字目から" + end_point + "文字目まで選択中" ); alert( "選択中の文字列は" + elem.value.substring( start_point, end_point ) ); } </script>
- 選択範囲のレンジ
- 選択範囲の始点までのレンジ
- 選択範囲の終点までのレンジ
という3つを作成して算出している。
(4)IEで,選択範囲を設定するサンプル
<input type="text" size=30 id="my_input" value="hogefuga"> <input type="button" onclick="f()" value="選択"> <script language="JavaScript"> function f() { var elem = document.getElementById( "my_input" ); setSelectionIE( elem, 1, 3 ); // ogが選択される } // 要素内の文字列を選択状態にする関数 // start, end : 選択したい文字列の開始地点と終了地点。(先頭からの文字数) function setSelectionIE( elem, start, end ) { // 終了地点を,末尾から数えた負の文字数に変換 var end_new = - ( elem.value.length - end ); // 範囲生成 var range = elem.createTextRange(); range.moveStart( "character", start ); // 先頭から数えた開始地点 range.moveEnd( "character", end_new ); // 末尾から数えた開始地点 // 選択 range.select(); } </script>
(5)IEで,クリックした文字を取得
ここまでのrangeの使い方を踏まえて,「テキストボックス内の文字列をクリックした時に,ちょうどクリックした所にある文字を取得」というのをやってみよう。
<input type="text" size=30 id="my_input" value="hogefugahigehage"> <!-- クリックした文字がここに表示されます。 --> <div id="d"></div> <script language="JavaScript"> function f() { // イベント情報を取得 var evt = arguments[0] || window.event; var elem = evt.srcElement; var clicked_pos = evt.offsetX; // 現在表示中の文字列を保管 var original_txt = elem.value; // 表示文字列を1文字ずつ増やしながら, // その文字列の幅とクリック位置を比較する。 var clicked_index = -1; for( var i = 1, len = original_txt.length; i <= len; i ++ ) { // 先頭から i 文字だけを表示 var sub_txt = original_txt.substring( 0, i ); elem.value = sub_txt; // その幅を取得 var range = elem.createTextRange(); var txt_max_pos = range.boundingWidth; // クリックしたときの位置が内包されているか? if( clicked_pos <= txt_max_pos ) { clicked_index = i - 1; break; } } // 表示文字列を復元 elem.value = original_txt; // クリックした文字を返す if( clicked_index >= 0 ) { var clicked_char = original_txt.charAt( clicked_index ); d.innerHTML = clicked_index + "文字目の" + clicked_char + "をクリックしました。"; } } // イベント割り当て my_input.onclick = f; </script>
アルゴリズムは,ソースコード中にコメントで示した通り。
画面上に実際に表示されている文字列をじかに変えてしまい,いろいろ長さを変えながらクリック位置を模索していく,というかなりトリッキーな方法。
こんな不思議な方法,実際に利用されるのだろうか?と思うかもしれないが,本当に利用されているのである。
jQueryで時刻を入力するためのプラグイン,「jQuery Time Entry」の中の「_doDblClick()」関数中のコードを見てみてほしい。
テキストボックス中のクリック位置によって,時・分のターゲットを切り替えている。
jQuery Time Entry
http://keith-wood.name/timeEntry.html
補足点
以上は,input要素で利用できるコード。
textareaの場合は,エラーなくカーソル位置を取得するコードが下記に紹介されている。
IEが \r\n を自動でトリムしてしまったかどうかを検出すればよい。
How do I get the current position of the cursor in a TEXTAREA using Javascript?
http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
※ http://d.hatena.ne.jp/authorNari/20070819/1187493863 にも関連情報がある。
また,クロスブラウザなコードを書くためには,下記サイトのrange関連の関数一覧対応表を参考に。
選択範囲の取得について調べた
http://d.hatena.ne.jp/dayflower/20080423/1208941641
Rangeオブジェクトが何者なのかについては,下記で基礎的な事がよくまとまっている。
JavaScript Rangeの使い方
http://wiki.bit-hive.com/tomizoo/pg/JavaScript%20Range%A4%CE%BB%C8%A4%A4%CA%FD
関連する記事:
JavaScriptの動かないコード (中級編) テーブルに行追加できない - 主に言語とシステム開発に関して
http://d.hatena.ne.jp/language_and_engineering/20080914/1221348848
JavaScriptで,文字列を反復する / 逆順に並び替える方法 - 主に言語とシステム開発に関して
http://d.hatena.ne.jp/language_and_engineering/20080924/1222174957
alert() と書くために,わざわざ行頭に戻らなくてもすむ方法 - 主に言語とシステム開発に関して
http://d.hatena.ne.jp/language_and_engineering/20100829/p1