prototype.jsの利用例:マウスオーバー時にテキストを編集できるようにする http://www.ark-web.jp/sandbox/wiki/174.html
prototype.jsの利用例:マウスオーバー時にテキストを編集できるようにする
お問い合わせフォームでは確認画面が表示されることが多いですが、その画面上で表示される各データに対してマウスオーバーすると即その場で修正できると便利かなと思い、その方法を探して、実装してみました。Google Calendarの予定詳細編集画面の挙動とイメージしていることは同じです。
最初に確認画面のHTMLの例を示します(ごくごく簡単に)
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script type="text/javascript" src="prototype.js"></script> </head> <body> <table> <tr> <td>会社名</td><td>アークウェブ</td> </tr> <tr> <td>お名前</td><td>進地</td> </tr> </table> <form action="" method="post"> <input type="hidden" name="company" value="アークウェブ" /> <input type="hidden" name="name" value="進地" /> <input type="submit" name="submit" value="送信" /> </form> </body> </html>
会社名とお名前という二つの項目が表示され、hiddenパラメータとしても持っているという非常にシンプルな例です。この会社名とお名前の値、例で言えば、アークウェブと進地にマウスオーバーした時に、それぞれが単なるテキスト表示からテキストボックスに変化するようにしたい。そして、値の書き換えを行い、そのまま送信ボタンを押すとお問い合わせフォームプログラムに対して書き換えた値を送信したい。ここまでが今回やりたいことです。prototype.jsを使うので冒頭でロードしておきます。また、文字コードはUTF-8にしておきます。
次に、マウスオーバーを補足するためにUIの準備をします。
<tr> <td>会社名</td><td id="company_val">アークウェブ</td> </tr> <tr> <td>お名前</td><td id="name_val">進地</td> </tr> : : <input type="hidden" id="company" name="company" value="アークウェブ" /> <input type="hidden" id="name" name="name" value="進地" />
ここでは単純に各値を格納するセルと値を保持しているhiddenタグを特定するためのIDをふっています。
次に、マウスオーバーによってテキストボックスに変更、フォーカスが他に遷移する時に表示値とhidden値の変更を行うクラス(EditableValue)を定義します。
<script type="text/javascript"> var EditableValue = Class.create(); EditableValue.prototype = { initialize: function(target_id) { this.text = $(target_id); this.input = document.createElement('input'); this.input.type = "text"; this.text.parentNode.appendChild(this.input); var hidden_id = this.text.id.substr(0, this.text.id.indexOf('_')); this.hidden = $(hidden_id); Event.observe(this.text, 'mouseover', this.enableInput.bindAsEventListener(this), false); Event.observe(this.input, 'blur', this.disableInput.bindAsEventListener(this), false); Element.show(this.text); Element.hide(this.input); }, enableInput: function() { this.input.value = this.text.innerHTML; Element.toggle(this.text, this.input); Field.focus(this.input); }, disableInput: function() { this.text.innerHTML = this.input.value; this.hidden.value = this.input.value; Element.toggle(this.text, this.input); }, }; </script>
initialize、enableInput、disableInputにはそれぞれ、EditableValueのコンストラクタ、マウスオーバー時に起動されるメソッド、フォーカスが他に遷移する時に起動されるメソッドを定義しています。
initializeでは、渡されたターゲットのID(例ではcompany_valやname_val)からprototype.jsの$()メソッドを使ってDOMのElementオブジェクトを取得して、this.textとしてセットします。また、inputタグのElementオブジェクトを定義して、this.textの親の子(つまりはthis.textの兄弟)としてthis.inputとしてセットします。値を保持しているhiddenのElementオブジェクトのidをターゲットのIDから求めて(例では、substringメソッドを利用してcompany_val、name_valからそれぞれcompany、nameを作成しています)、$()メソッドを使って当該hiddenオブジェクトをthis.hiddenとしてセットします。
さらに、EventListenerを利用して、this.textに対してmouseoverイベントが発生した場合にenableInputに定義したメソッドを起動するように、this.inputに対してblurイベントが発生した場合にdisableInputに定義したメソッドを起動するようにセットします。
initializeでは最後に、this.textを表示、this.inputを非表示にセットします。
マウスオーバーイベントがthis.textに対して発生した場合、enableInputに定義したメソッドが呼ばれます。このメソッドでは、this.inputの値にthis.textが保持する値(=現在表示している値)をセットし、this.inputを表示、this.textを非表示に切り替えています。
フォーカスがthis.inputから外れた場合、disableInputに定義したメソッドが呼ばれます。このメソッドでは、this.inputの値をthis.textが保持する値とthis.hiddenの値にセットし、this.inputを非表示、this.textを表示に切り替えています。
最後に、このEditableValueクラスにそれぞれのターゲットIDを渡してnewします。
<script type="text/javascript"> new EditableValue('company_val'); new EditableValue('name_val'); </script>
以上で、マウスオーバーによって当該値が編集可能になり、フォーカスアウトによって編集した値によって表示、およびhiddenにセットされた値が変更されます。JavaScriptを仮にOffにしたクライアントであっても、単にこのマウスオーバーによる編集が使えなくなるだけで従来の動きは保障されます(=デグレード可能なAjax)。
今回の例のようにクラスとEventListenerを使うと、下記のようにターゲットのタグに対してイベントを記述しなくても単にidを振るだけでコントロール可能になるため、HTMLは汚れず、イベントによる遷移もクラス内で集中管理できて構築がとても楽になります。
<tr> <td>会社名</td><td id="company_val" onmouseover="hoge(this)">アークウェブ</td> </tr> <tr> <td>お名前</td><td id="name_val" onmouseover="hoge(this)">進地</td> </tr>
参考 †
補足 †
mouseoverとblurの組み合わせだと、FireFoxでは(およびGecko-engineのブラウザでは)
Permission denied to set property XULElement.selectedIndex NS_ERROR_XPC_JS_THREW_STRING
というExceptionが発生します。
http://groups.google.ca/group/netscape.public.mozilla.dom/browse_thread/thread/821271ca11a1bdbf/46c87b49c026246f?lnk=st&q=+focus+nsIAutoCompletePopup+selectedIndex&rnum=1
http://www.macridesweb.com/oltest/FocusProblems.html
対応方法としては、formタグに対して、autocomplete="off"を指定します。
今回の例では
<form method="post" action="" autocomplete="off">
とします。
tag:Ajax, prototype.js