テキストをクリック→テキストボックスを表示→インラインで編集→テキスト更新を実現するクラスを作ってみた。
なるべく小さな部品をクラスという形で提供して,再利用しやすい俺ライブラリをこまめに作っていこうと思います。id:xoinu:20071231 でやろうとしていたこととは少し外れるので,エト記に書いておきます。
表題の通り,今日作ってみたのは,はてなグラフの編集画面にあるような UI で
- 始めの見た目は文字列
- 文字列をクリックすると,テキストボックスになり,編集できるようになる
- Enter キー,あるいは focus が外れると編集終了
- 編集が終わると,テキストボックスが文字列に戻って,編集後の文字列になる
- 編集後に hook 関数を起動する
というものです。
文字列は
<span id="hoge">hogehoge</span>
のような id 付きのブロックで囲まれていると想定しています。これに対して
var ie; addEvent(window, "load", pageInit); function pageInit() { ie = new InlineEdit($("hoge")); }
みたいな感じで,適応対象のテキスト要素に対して,1つインスタンスを作って使います。addEvent という関数は汎用のイベント登録関数です。prototype.js も使ってます。
Enter キーを押すとフォームが submit されてしまうので,preventDefault() 関数で回避しています。いちおう,Safari と Firefox で動作確認済。参考にしたのは,はてなグラフの実装(Firebugで赤裸々に解析)ですが,スタイルシートを使用せず,インスタンスがDOM要素を保持して,イベントの度にホイホイすげ替えるようしていいます。IEだと多分動かないと思います。
クラスの定義は以下の通りです。
// // text => <span>...text node...</span> // var InlineEdit = Class.create(); InlineEdit.prototype = { text : null, form : null, onStartFinish : function(){}, onEndFinish : function(){}, onComplete : function(){}, initialize : function(text, onComplete, onStartFinish, onEndFinish) { var parent = text.parentNode; var form = document.createElement("form"); var input = document.createElement("input"); input.setAttribute("type", "text"); form.appendChild(input); form.setAttribute("autocomplete", "off"); this.form = form; this.text = text; if (onStartFinish) this.onStartFinish = onStartFinish; if (onEndFinish) this.onEndFinish = onEndFinish; if (onComplete) this.onComplete = onComplete; var _this_ = this; addEvent(this.text, "click", function() { _this_.start(); }); addEvent(this.form.firstChild, "blur", function() { _this_.finish(); }); addEvent(this.form.firstChild, "keypress", function(e) { if (0 == e.charCode || 13 == e.charCode) {_this_.form.firstChild.blur(); e.preventDefault(); } }); addEvent(this.form, "submit", function() { _this_.finish(); }); }, start : function() { var parent = this.text.parentNode; if (parent) { var tx = this.text.firstChild.nodeValue; this.form.firstChild.setAttribute("value", tx); this.form.firstChild.setAttribute("size", tx.length); parent.replaceChild(this.form, this.text); this.form.firstChild.focus(); this.form.firstChild.select(); } }, finish : function() { this.onStartFinish(); var parent = this.form.parentNode; if (parent) { this.onComplete(); this.text.firstChild.nodeValue = this.form.firstChild.value; parent.replaceChild(this.text, this.form); } this.onEndFinish(); } };
コンストラクタの第2引数以降で,finish() メンバ関数内で呼ばれる hook を登録することができます。実際には例外処理もしなければいけませんね。