Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。
textareaの中で選択した文字列の前後に、任意の文字列を挿入するJavascript。流れとしては、まず選択領域の始点と終点の位置を取得し、その境界を基準にしてtextareaの文字列全体を分割。そして加工して組みなおす。
始点と終点の位置は、Mozillaおよび(最近の)Opera/Safariでは容易に取得できる。問題はIE。IEのTextRangeオブジェクトは、後のコンテクストを見て処理を行うことが難しいため、特にtextarea内の文字列を操作したい場合は厄介です。
逆に言えば、始点と終点の位置さえ判ればなんとかなるので、いろいろ試してみてなんとか形に出来ました。
Sample
memo: サンプルのテキストは、以前書いた記事「環境は人を造るか?」より抜粋。
Script
function getAreaRange(obj) {
var pos = new Object();
if (isIE) {
obj.focus();
var range = document.selection.createRange();
var clone = range.duplicate();
clone.moveToElementText(obj);
clone.setEndPoint( 'EndToEnd', range );
pos.start = clone.text.length - range.text.length;
pos.end = clone.text.length - range.text.length + range.text.length;
}
else if(window.getSelection()) {
pos.start = obj.selectionStart;
pos.end = obj.selectionEnd;
}
return pos;
// alert(pos.start + "," + pos.end);
}
var isIE = (navigator.appName.toLowerCase().indexOf('internet explorer')+1?1:0);
ここが要。選択領域の位置を取得している部分。既存の選択領域とは別の選択領域を用意し、新しい選択領域でtextareaを選択。始点あるいは終点を操作し、2つの選択領域の文字数をカウントすることで位置を割り出す。
note: 「clone.setEndPoint( 'EndToEnd', range );
」で、「cloneの終点をrangeの終点に合わせる」の意。
function surroundHTML(tag, obj) {
var target = document.getElementById(obj);
var pos = getAreaRange(target);
var val = target.value;
var range = val.slice(pos.start, pos.end);
var beforeNode = val.slice(0, pos.start);
var afterNode = val.slice(pos.end);
var insertNode;
if (range || pos.start != pos.end) {
insertNode = '<' + tag + '>' + range + '</' + tag + '>';
target.value = beforeNode + insertNode + afterNode;
}
else if (pos.start == pos.end) {
insertNode = '<' + tag + '>' + '</' + tag + '>';
target.value = beforeNode + insertNode + afterNode;
}
}
境界の座標が判れば、後は「分割 → 追加 → 再統合」の流れでOK。この場合は選択領域だけでなく、まだ選択していない状態でもキャレット(文字の入力位置を示すポインタ)の位置に挿入できるようになっている ...が、この操作は関数getAreaRange()にて「obj.focus()
」していないとIEでは巧くいかなかったりする。注意。
参照: Experts Exchange, IE - textarea caret position
Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。
30 Comments
お役に立てて良かったです!
コメント thx! 2008–01–12 16:51
TinyMCE等のリッチテキストエディタでいろいろやろうと考えてましたが、もっと軽量で
スタイリッシュなものをと望んでいたところ、このサイトを見つけました。
これをもとに、いいものを作りたいと思います。本当にありがとうございました。 2008–05–29 13:00
どうぞ心ゆくまで使い倒してやってください^^b 2008–06–02 20:45
こちらのスクリプトがとても参考になりました。
ありがとうございます。 2008–06–26 15:40
いや、意外と需要あるんですね、コレ。
モチベ上がってきた...! 2008–06–28 05:07
使わせてもらいます、本当にありがとうございました。 2008–07–12 23:40
var target = document.getElementById(obj);
この要素取得については別の関数にしたかったので、
勝手ながら若干改造しました^^ 2008–08–02 20:30
ありがとうございます。
早速、使わせて頂きます。 2008–10–24 22:48
ありがとうございました。 2008–12–16 15:35
ありがとうございました。 2009–01–05 16:49
参考にさせていただきます。
どうもありがとうございます。 2009–02–28 22:18
これを参考させていただきますね。
ありがとうございました。 2009–06–02 14:24
本当に困っていたので助かりました^-^b 2009–06–22 17:19
心より感謝です…。ありがとうございます! 2009–09–17 07:23
個人または家族で使える自宅用のWebポータルシステム「MyHome Portal」をオープンソースとして公開しています。
このMyHome Portalのカレンダーのスケジュール入力画面で、スクリプトを利用させていただきました。
http://ok2nd.blog87.fc2.com/blog-entry-129.html
Chrome、Safariでtextarea内の文字列選択がうまくいかない問題がやっと解決できました。
ありがとうございました。 2009–10–03 07:32
助かりました。ありがとうございました。 2010–02–15 20:09
ありがたく使わせていただきます!! 2010–03–26 12:07
文字列を挿入後、カーソル(キャレット)が、textarea内の挿入文字の後ろに残る修正版を作成しました。継ぎ接ぎ的なロジックになってしまいましたが、とりあえず、公開しました。
http://ok2nd.blog87.fc2.com/blog-entry-246.html 2010–12–21 18:14
この機能がこんな少しのスクリプトで実現できるとは驚きです。
ありがたく使わせていただきます。 2011–02–01 16:25
こちらのソースですが、textarea以外のタグ(divやbody)で入力可能にしている場合にも使いたいのですが、
対応可能でしょうか。 2011–03–09 15:40
contentEditableな選択領域は、textareaの選択領域の場合と取り方が違うんです。
まずそのままじゃ使えないよ、というのと、
タグの重複とか考えるといろいろ面倒なので、素直にexecCommand()使ったほうが良いと思います。 2011–03–10 02:04
なるほど。
execCommandですね。
はじめて知りました^^;
実は以下の処理を使っていまして、
http://d.hatena.ne.jp/okinaka/20090727/1248671860
これを使ってテキストエリアが対象の場合はイケました。
こちらを「contentEditableな選択領域」にも効くようにしたいんです。
$("body").insertAtCaret(HTMLが入った変数hoge);
で失敗してます。
トライ中の対象はMTのリッチテキストエリアです。
いろいろとやってみたのですが、
アドバイスできそうな事があれば、伺いたくお願い申し上げます。 2011–03–10 18:12
iframe内のドキュメントの取り方が間違ってました。
純粋なJsとJQueryで記述が微妙に異なるところがネックになってました。
こちらの記事もとても助かりました。
今後も参考にさせていただきます。
ありがとうございました。 2011–03–11 10:32
まさに探していたものでしたので、ありがたく使わせて頂きます。
動作テストしていて気になったのが何も選択せずに連続してタグを挿入するとキャレットがずれるバグ
は「ok.2nd」さんの所で見事に解決されていましたのでとても参考になりました。
この場を借りてお二人に感謝いたします。
ありがとうございました。 2012–02–21 14:48