■ prototype.js の Form.Element.Serializers をハックしてみたよ
思いっきり乗り遅れ感抜群って感じなのですけど、昨日から prototype.js を使った JavaScript のコーディングをやり始めました。
moo.fx とか、Yahoo のライブラリ とか色々見てまわったんだけど、結局 prototype.js + script.aculo.us が一番お手軽高機能っぽく感じたのでこれを使う事にしました。
そんでもって、今まで自前でやっていた処理が組み込みの関数で出来るんだ、便利だなスゲェなって思って見ていたんだけど、Form.serialize の機能に不満点がある事が判明。実際自分もここでは苦労したんだけど、同じ所がおろそかになっていました。
Form.serialize は、フォームのフィールド名と値をリストにして返す処理をしてくれて、Ajax とかでフォームの内容を送信する場合に便利なのですが、SUBMIT ボタンが複数ある場合に期待した通りに動作してくれないようです。
普通はクリックした SUBMIT ボタン以外の情報は全く送信されない*1のですが、Form.serialize は全ての SUBMIT ボタンの情報を返してきます。
実際にこれで何が困るのかって言うと、クリックした SUBMIT ボタンの内容で処理を分岐したりしている場合に、クリックしていないボタンの情報が送られて来ると分岐処理がうまくいかなくて期待した通りに動作しなくなってしまいます。
対処方法はないかと検索して、Multiple AJAX submit buttons in a Rails form とか書いてある記事を発見しましたが、何やらちょっと強引に対処しているような感じで明解な解決方法はないようです。
というわけで、prototype.js の後に読み込ませるだけで「クリックした SUBMIT ボタンの情報だけを送信するように Form.serialize の処理を上書きする」ようなコードを書いてみました。
実は一点解決出来ない問題があって、ウィンドウのロード後にフォームオブジェクトを動的に生成した場合、そこで生成した SUBMIT ボタンのクリックを検出する事が出来ません。なので動的生成を行なった場合は、毎回 Form.SubmitFlagger.initialize() を呼び出さないと期待通りに動作しません。ていうか、むしろ initialize してくれないと SUBMIT の情報が誤って送信されるようになっちゃいます。
こんなに一生懸命文章も書いたっていうのに、正直不便になってます。「ハックしてみたよ」じゃねーよって感じ。
タイマーを使って監視すればうまく動くでしょうが、それは綺麗なやり方じゃないから出来ればやりたくありません。だから、誰かもっとうまいやり方を考えてって事なの!なんつーか、もっと根本的にクリックしたかどうかを判定する楽な方法は無いんでしょうかねえ。
03.13 09:29 追記: 4行目の if を書き換えて、動的生成を行なった場合に限っては従来通りの動作をするようにしました。これで「不便になった」って言う状況は避けられたかと。
03.13 16:50 追記2: IE で動いてなかったので、func の処理を修正した。ついでに MacSafari でも動作検証。
var __oginput = Form.Element.Serializers.input; Form.Element.Serializers.input = function(element) { if(element.type.toLowerCase() == 'submit' && element._lclick) return this.submit(element); return __oginput(element); } Form.Element.Serializers.submit = function(element) { if(Form.SubmitFlagger.getValue() == element._lclick) return [element.name, element.value]; } Form.SubmitFlagger = { initialize: function() { var func = function(val) { return function() { Form.SubmitFlagger.setValue(val); }; } var inputs = document.body.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; if (input.type.toLowerCase() == 'submit') { input._lclick = i; Event.observe(input, 'click', func(i), false); } } }, setValue: function(val) { return this.button = val; }, getValue: function() { return this.button; } } Event.observe(window, 'load', Form.SubmitFlagger.initialize, false);
- *1: 普通って書いたけど、RFS とかそういうので定義されているのかまでは調べてなくて適当に書いてますけど
Posted by Kyosuke Takayama at 2006-03-12 (Sun) 22:11 printable version