二重submit防止のjavascriptをjQueryで。
「ボタンの二度押し対策」とか「二重送信の防止」と呼ばれる設計は、Webアプリにおいてとても重要だ。
なんでもかんでもダブルクリックするユーザーというのは驚くほど多くて、そうした人々はデスクトップ上のアイコンとWebページ上のsubmitボタンとを区別しない。 あるいはsubmitボタンを押したあとで十数秒たっても反応が無い=ネットワーク遅延とかサーバがのろいとか=の場合にも、もう一度ボタンを押したくなるのは当然の心理だろう。
二度押しの結果として同じ商品が2個届いてしまうような事態は避けたい。だからこそ、Web業界ではいろいろと対策が練られてきた。input type=hiddenのタグとサーバ側セッション情報とに同じトークン文字列を持たせて云々、というのがサーバ側での定番の手法。そして、クライアント側ではJavaScriptを使った手法が様々に考えられている。 サーバ側での対応は、アプリの誤作動や、それこそ注文伝票が2枚発生してしまうのを防ぐためだ。一方クライアント側での対応はユーザビリティ向上の側面が大きい。どっちの対応も正しく、そして必ず両方とも実装する必要がある。
ここではクライアントサイドでJavaScriptを使う手法の話をする。何年も前に議論がなされていて、すでに枯れた話題ではあるのだが、ブラウザは多様化(特に携帯とか)し、JavaScript開発での汎用ライブラリの活用は当たり前になってきたこの時代、どんな方法がシンプルかつベターなのかという観点で、あらためて手法の一つを紹介したい。
数ある方法論のなかで、最近気に入っているのが、これ。
jQuery Disable On Submit Plugin | EvanBot
/* * jQuery Disable On Submit Plugin * http://www.evanbot.com/article/jquery-disable-on-submit-plugin/13 * * Copyright (c) 2009 Evan Byrne (http://www.evanbot.com) */ $.fn.disableOnSubmit = function(disableList){ if(disableList == null){var $list = 'input[type=submit],input[type=button],input[type=reset],button';} else{var $list = disableList;} // Makes sure button is enabled at start $(this).find($list).removeAttr('disabled'); $(this).submit(function(){$(this).find($list).attr('disabled','disabled');}); return this; }; 使い方↓(head内のscriptタグで) $(function(){ $('form').disableOnSubmit(); // 全てのformに作用させる場合 $('#hoge').disableOnSubmit(); // 特定のid属性をセットしたformタグに作用させる場合 $('.hoge').disableOnSubmit(); // 特定のclass属性をセットしたformタグに作用させる場合 });
submitボタンを押した瞬間にそれをdisabled(グレイアウト)にすることで二度押ししようにもできなくする、jQueryを使ったスクリプトである。おすすめする理由は次のとおり。
- jQueryだから。どうせ他の部分でもjQuery使っていることがあるので、そのついででシンプルなスクリプトで済むのなら、気分的にもラクだ。
- オリジナルにスクリプトを書こうとして素直に disabled=true とやるだけだと、次にいったページから「戻る」ボタンで戻られるとdisabledになったまま(Firefoxの場合)というワナがあるのだが、こいつはその点も考慮されている。
-
どのformに作用させるかを、html側のデザイン次第でどうにでもできる。ページ内の全てのformではなく特定のformにだけ使いたいという場合もあるだろう。
- 例えばform id="foo"とform id="bar"の二か所だけに絞りたければ、
$('#foo').disableOnSubmit();
$('#bar').disableOnSubmit();
と2行書けばいい。 -
HTMLでは、id属性はwebページ内での重複が許されない。同一ページ内に複数のformがあって、それらにいちいち違うid属性を与えるのが面倒だというのであれば、class属性を使うのがいいだろう。form class="foo" というclass属性を与えたformタグすべてに対して作用させたければ
$('.foo').disableOnSubmit();
の1行で済む。また、cssではclass属性はタグに複数個設定できる。つまり form class="aaa bbb" みたいにできるのだから、なんだったらデザイン(見栄え)制御用のclass属性とは別にdisableOnSubmit()専用のclass属性を追加してしまえばいい。
- 例えばform id="foo"とform id="bar"の二か所だけに絞りたければ、
- form onSubmit="hogefunction()" のようにonSubmit属性でJavascriptを呼び出す手法をよく見かける。しかしこれはちょっと危険で、ブラウザの種類によってはJavaScript関数どころかform全体が動かなくなるケースがあるらしい。少し古いケータイ向けフルブラウザに多い。これはinput type="submit" value="hoge" onClick="hogefunction()" のようにonSubmitではなくonClickでやっても似たような危険が伴う。ケースバイケースなのでなんとも言えないが、少なくとも formタグにはid属性やclass属性を指定するだけという手法ならその部分は普通のHTMLに過ぎない。よって、特殊なブラウザであっても動作不良を起こす可能性が低くなるんじゃなかろうかと。
- すぐにやめられる(笑)。「やってみたけど気にいらなかった」というときは、headタグ内のJavaScriptを消すだけだ。使わないid属性やclass属性をformタグに残したところで、どんなブラウザであれ動きにも見栄えにもまったく影響は残らない。
なお、クリックしたときにsubmitボタンをdisabledにしてしまう、という手法それ自体に、 「submit ボタン disable 技の罠 - naoyaのはてなダイアリー」という問題があることは一応頭にいれておいたほうがいい。 しかし、submitボタンのvalue値でサーバ側の動作が変わるような設計は意外と見かけない。つまり、submitボタンのvalue値は「ボタン上に表示される文字」としてしか使ってないケースのほうが多いのではないだろうか。だとすると、そう深く心配するほどでもないだろう。
さらに、これはもはや蛇足だろう(であってほしい)が、下のような書き方をしているformでは、この方法は動作しない。
<table> <form action="..."> <tr> ......
HTMLとしてありえない位置にformタグがある。 Transitionalならいいのかもしれないけど、strictなdoctypeだとするとおかしい。 そしてjQueryはstrictな方針でDOM要素をたどっていく(らしい)ので、こういう非準拠な書き方をされるとdocumentの中のform要素をうまく捕捉できないことがある。結果、このformではdisableOnSubmit()が動かない。 「formタグの上下に空白ができてしまうんだ、どうしたらいい?」という相談に対する答えとして、ひと昔、いやふた昔前くらいにごく一部のWeb屋の間で流行ってしまった手法だ。 CSSくらい使えよと。(正解は、form style="margin:0;" でいい) 古いページ/デザインだといまだにこういうのが残っていたりすることも頭の隅にいれておこう。
コメント
submitボタンのvalueで処理を分ける実装はあまり見かけないという点には同意しますが、Javaの世界だと例えばSAStrutsやCubby等、submitボタンのnameで処理を分ける実装は割とある気がします。
Posted by Anonymous at 2010年2月 4日
コメントする
(初めてのコメントの時は、コメントが表示されるためにこのブログのオーナーの承認が必要になることがあります。承認されるまでコメントは表示されませんのでしばらくお待ちください)