by WebSurfer
2012年1月9日 23:04
JavaScript を使って textarea のキャレット(カーソル)位置に文字列を挿入するサンプルです。
挿入する文字列を入力するために jQuery UI の dialog を利用していますが、それ以外は JavaScript のみを使用しています。
Firefox などの場合は、選択された部分の先頭、末尾の index (整数型)を、それぞれ selectionStart、selectionEnd で取得できますが、IE の場合はそれに該当するプロパティがないのが問題です。
IE の場合は、document.selection プロパティで selection オブジェクト(textarea 要素そのものではなく、textarea 要素の中の選択された文字の部分)を取得し、さらに createRange メソッドを使って TextRange オブジェクトを作成し、それを操作することになります。
TextRange オブジェクトについては、MSDN ライブラリの TextRangeオブジェクトの使用 を参照してください。
そのサンプルコードは以下の通りです。上の画像は、このコードを実行したときのブラウザの画面です。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>textarea のキャレット位置に文字列を挿入</title>
<script src="Scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
<script src="Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>
<link href="css/smoothness/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
//<![CDATA[
// IE の場合、[ダイアログ表示]ボタンクリックの時点で
// textarea 内の選択された文字部分の TextRange オブジェ
// クトを取得して維持しておく。そうしないと、textbox に
// 入力中に textarea からフォーカスが外れ、選択範囲がリ
// セットされてしまう。
// Firefox 等は textarea からフォーカスが外れても、選択
// 範囲は維持されるので、このような処置は不要。
var textRange = null;
function getTextRange(textarea) {
// textarea の文字が選択されてない場合はフォーカスを
// 当てないとうまくいかない。
textarea.focus();
// document.selection プロパティで selection オブジ
// ェクト(testarea の中の選択された文字の部分)を取
// 得する。文字が選択されていない場合、キャレット位
// 置の空の selection オブジェクトになる。
// selection オブジェクトに何か処理を行う場合は、
// createRange メソッドで TextRange オブジェクトを作
// 成して処理する。
return document.selection.createRange();
}
function insertText(textarea, txt) {
if (textRange == null) {
// IE 以外の場合
// 選択部分の先頭の index と長さを取得
var index = textarea.selectionStart;
var length = textarea.selectionEnd - index;
// 文字列を挿入
textarea.value = textarea.value.substr(0, index) +
txt + textarea.value.substr(index + length);
// キャレット位置を挿入した文字列の最後尾に移動
textarea.focus();
var newCaretPosition = index + txt.length;
textarea.setSelectionRange(
newCaretPosition, newCaretPosition);
} else {
// IE の場合
// TextRange.text プロパティにダイアログの文字列を
// 設定し、testarea の中の選択された文字部分を置き
// 換える。
textRange.text = txt;
// キャレット位置を挿入した文字列の最後尾に移動
textRange.select();
}
}
// ---------------------------------------------------
// 挿入する文字列の入力用に jQuery UI の dialog を利用
$(function () {
$("#dialog").dialog({
bgiframe: true,
autoOpen: false,
modal: true,
resizable: false,
buttons: {
'Insert': function () {
insertText(
$('#textarea').get(0),
$('#textbox').val()
);
$(this).dialog('close');
},
'Cancel': function () {
$(this).dialog('close');
}
},
open: function () {
$('#textbox').val('')
}
});
$('#buttonShowDialog').click(function () {
if (document.selection != undefined) {
textRange = getTextRange($('#textarea').get(0));
}
$('#dialog').dialog('open');
});
});
//]]>
</script>
</head>
<body style="font-size: 12px;">
<h1>キャレット位置に文字列を挿入</h1>
<textarea id="textarea" cols="50" rows="5">textarea のキャレット位置に文字列を挿入します。</textarea><br />
<button id="buttonShowDialog">ダイアログ表示</button>
<div id="dialog" title="挿入する文字列">
<p>ここに入力:<br />
<input type="text" id="textbox" size="30" /></p>
</div>
</body>
</html>
textarea の文字列が選択されている場合、その文字列を dialog に入力した文字列で置き換えます。文字列を選択していない場合(キャレットのみの場合)、キャレットの位置に dialog に入力した文字列を挿入します。
IE9, Firefox 9.0.1, Safari 5.1.2, Opera 11.60 で検証して期待通りに動くことを確認しました。(その他のブラウザは検証してないです)
by WebSurfer
2011年11月20日 15:47
リストボックスの内のアイテムのスクロール位置をポストバック前後で維持する方法です。IE9, Firefox 8.0, Safari 5.1.1, Opera 11.52 で検証してみましたが、Opera 以外は期待通りの動きをしました。(Opera では scrollTop が取得できず、うまくいきません。他の方法を検討中です)(2011/11/21 追記: IE6 もダメでした。IE7, IE8 は OK でした)
IE ではリストボックスの中の項目を選択するしないにかかわらず、ポストバックすると一番先頭に戻ってしまいます。その他のブラウザでは、リストボックスの中の項目を選択すればポストバック後に一番先頭に戻ることはありませんが、スクロール位置はポストバック前後で変わってしまいます。
今回の例では、JavaScript の scrollTop を使用して、ポストバック直前のリストボックスのスクロールバーの上端の座標を取得して隠しフィールドに保存し、ポストバックして再描画する時に隠しフィ��ルドに保存した座標を取得してスクロール位置を設定しています。
そのサンプルコードは以下の通りです。上の画像は、このコードを実行したときのブラウザの仮面です。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。
隠しフィールドには ASP.NET の HiddenField サーバーコントロールを使いました。これを使うと自動的に ViewState によってポストバック前後の Value が維持されます。普通の html の <input type="hidden" ... /> を使う場合は、ポストバック前後の value の維持に一工夫必要です。
リストボックスには、これも ASP.NET の ListBox サーバーコントロールを使っていますが、スクロール位置をポストバック前後で維持する仕組みに関しては、普通の html の select でも同じです。
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void SubmitBtn_Click(Object sender, EventArgs e)
{
string selectedItems = String.Empty;
int count = 0;
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected)
{
selectedItems += ListBox1.Items[i].Value + ", ";
count++;
}
}
if (count > 0)
{
selectedItems =
selectedItems.TrimEnd(new char[] { ',', ' ' });
Label2.Text = "You chose: " + selectedItems;
}
else
{
Label2.Text = "アイテムが選択されていません。";
}
}
protected void Page_Load(object sender, EventArgs e)
{
string value = HiddenField1.Value;
Label1.Text = "Scroll Top: " + value;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/jquery-1.4.1.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
// Opera 11.52 で動かない(scrollTop が取得できない)
function setScrollTopInHiddenField() {
var position =
document.getElementById("<%=ListBox1.ClientID%>").scrollTop;
document.getElementById("<%=HiddenField1.ClientID%>").value =
position;
}
window.onload = function () {
var position =
document.getElementById("<%=HiddenField1.ClientID%>").value;
if (position != "") {
document.getElementById("<%=ListBox1.ClientID%>").scrollTop =
position;
}
}
// jQuery を使っても、やはり Opera では動かない
// function setScrollTopInHiddenField() {
// var position =
// $("#<%=ListBox1.ClientID%>").scrollTop();
// $("#<%=HiddenField1.ClientID%>").val(position);
// }
// $(document).ready(function () {
// var position =
// $("#<%=HiddenField1.ClientID%>").val();
// if (position != "") {
// $("#<%=ListBox1.ClientID%>").scrollTop(position);
// }
// });
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:HiddenField ID="HiddenField1" runat="server" />
<asp:Label ID="Label1" runat="server">
</asp:Label>
<br />
<asp:ListBox id="ListBox1"
Rows="10"
SelectionMode="Multiple"
runat="server">
<asp:ListItem>Item 1</asp:ListItem>
<asp:ListItem>Item 2</asp:ListItem>
<asp:ListItem>Item 3</asp:ListItem>
<asp:ListItem>Item 4</asp:ListItem>
<asp:ListItem>Item 5</asp:ListItem>
<asp:ListItem>Item 6</asp:ListItem>
<asp:ListItem>Item 7</asp:ListItem>
<asp:ListItem>Item 8</asp:ListItem>
<asp:ListItem>Item 9</asp:ListItem>
<asp:ListItem>Item 10</asp:ListItem>
<asp:ListItem>Item 11</asp:ListItem>
<asp:ListItem>Item 12</asp:ListItem>
<asp:ListItem>Item 13</asp:ListItem>
<asp:ListItem>Item 14</asp:ListItem>
<asp:ListItem>Item 15</asp:ListItem>
<asp:ListItem>Item 16</asp:ListItem>
<asp:ListItem>Item 17</asp:ListItem>
<asp:ListItem>Item 18</asp:ListItem>
<asp:ListItem>Item 19</asp:ListItem>
<asp:ListItem>Item 20</asp:ListItem>
<asp:ListItem>Item 21</asp:ListItem>
<asp:ListItem>Item 22</asp:ListItem>
<asp:ListItem>Item 23</asp:ListItem>
<asp:ListItem>Item 24</asp:ListItem>
<asp:ListItem>Item 25</asp:ListItem>
</asp:ListBox>
<asp:button id="Button1"
Text="Submit"
OnClick="SubmitBtn_Click"
OnClientClick="setScrollTopInHiddenField();"
runat="server" />
<br />
<asp:Label ID="Label2" runat="server">
</asp:Label>
</div>
</form>
</body>
</html>
@ Page ディレクティブに MaintainScrollPositionOnPostback 属性というものがありますが、たぶんこんな感じでポストバック前後の位置を維持しているのではないかと思います。
------ 2016/1/12 追記 ------
MaintainScrollPositionOnPostBack(ポストバック前後で上下左右のスクロール位置を維持する機能)の仕組みを調べてみました。
やはり、この記事に書いたようなスクロール位置を保存する隠しフィールドとクライアントスクリプトを使用しています。
MaintainScrollPositionOnPostBack でスクロール位置を維持する仕組みについては別の記事「ポストバック前後でスクロール位置維持」に書きましたので興味がありましたら見てください。
by WebSurfer
2011年7月23日 14:09
IE にはオートコンプリートという機能があって、TextBox に前回入力した文字列を入力中に補完してくれます(Firefox など他のブラウザにも同等の機能がありますが、今回は IE に限った話です)。
TextBox に入力した文字列が IE のオートコンプリートに登録される条件は、その TextBox が form の中にあって、form が submit タイプのボタン(例: <input type="submit" ... )のクリックで submit されることです。
button タイプのボタン(例: <input type="button" ... )の onclick 属性にスクリプトを設定して、そのスクリプトで form を submit しても、オートコンプリートには登録されません。
詳しくは、マイクロソフトサポートオンラインのページ BUG: AutoComplete Does Not Work When You Use Script to Submit a Form を参照してください。
ただし、上記のページにも出ていますが、AutoCompleteSaveForm Method を利用すると、submit せずにオートコンプリートに登録することができます。
具体的なコード例は以下の通りです。(上記の AutoCompleteSaveForm Method のコードを ASP.NET の TextBox を使って書き換えただけです)
なお、AutoCompleteSaveForm メソッドは IE5 以降のバージョン専用だそうですので注意してください。もちろん、Firefox, Chrome などでは動きません。
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript">
//<![CDATA[
function fnSaveForm() {
window.external.AutoCompleteSaveForm(form1);
document.getElementById("AutoCompleteTest").value = "";
document.getElementById("AutoCompleteIgnore").value = "";
}
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
This text is saved:
<asp:TextBox ID="AutoCompleteTest"
runat="server">
</asp:TextBox>
<br />
This text is not saved:
<asp:TextBox ID="AutoCompleteIgnore"
runat="server"
AutoCompleteType="Disabled">
</asp:TextBox>
<br />
<input id="Button1"
type="button"
value="Save Value"
onclick="fnSaveForm()" />
</div>
</form>
</body>
</html>