jQueryのclone関数の思わぬバグ!セレクトボックス実装時は注意!入力値のコピー処理がnullになる不具合の解決方法!
jQueryのライブラリの中で、clone関数というHTMLオブジェクトをコピーする便利なものがあります。
ウェブページを構築する際に、利用者の入力内容に応じて、
リクエストさせたいデータ項目をフレキシブルに切り替えさせたいときによく利用しています。
具体的には、divコンテナに入力情報用のHTMLオブジェクトを記述し、
サブミット処理などでリクエストを送信する際に、
divコンテナの子要素のオブジェクトをformタグ内にクローンさせて、入力情報を送信させるというものです。
ちょっと大げさな例を紹介しますが、
シンプルな処理なら以下の画像のように、formタグを複数用いてデータ送信するだけで済みます。
ところが送信先の処理がどれもほぼ共通化できる状態で、
わざわざ複数に分けてコーディングするとあまりかっこよくありませんよね。
さらに大量の入力データを扱う場合、ソースが増え見づらいし、管理しづらいのが問題です。
これを解決してくれるのに便利なのがclone関数です。
formタグをひとつに集約し、formタグの子要素は指定しません。(指定する場合もあり)
各種入力内容のオブジェクトは例えばdiv要素のコンテナにまとめてしまい、
入力した内容に応じて、div要素内の入力情報をサブミット時にformに移してあげれば、かなりスッキリしますね。
ポップアップ入力やその他複雑な処理を実装する際にデータの移し替えに利用していますが、
clone関数を使ったことある人なら知ってる思わぬ不具合があります。
セレクトボックスの入力値がjQueryのclone関数でnullになる不具合
jQueryの最新バージョンを使っていないので、
もしかしたら修正されているかもしれませんが、まだまだ旧バージョンのjQueryを使う機会は多いと思います。
実はこのclone関数の不具合でセレクトボックスのオブジェクトをコピーすると、
選択入力された値がコピー先で反映されずに空状態になってしまいます。
公式の不具合のようで、clone関数の1行のコーディングではセレクトボックスの入力値がコピーされないんです。
セレクトボックスそのものはコピーできても、選択した入力値を保持していません。
どうやらこの問題はブラウザによって挙動が違うようでjQuery側のプラグインで制御できないみたいなんです。
試しに以下のようなHTMLに対してスクリプトコードを記述します。
clone関数実行前のHMTL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <form name="submitForm" id="submitForm"> </form> <!-- コピーするHTMLオブジェクト --> <div class="container"> <input type="text" name="field1" value="2015" /> <input type="text" name="field2" value="12" /> <select name="field3"> <option id=""></option> <option id="1" selected="selected">ノートPC</option> <option id="2">デスクトップPC</option> <option id="3">タブレットPC</option> </select> </div> <input type="button" id="regist-button" value="登録" /> |
スクリプトコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <script type="text/javascript"> $(document).ready(function() { // 登録ボタン押下時の処理 $("#regist-button").click(function() { // 入力情報をクローンしてformにコピー $("div[class='container']").clone(false) .appendTo("form[name='submitForm']"); // フォームのサブミット処理 $("form[name='submitForm']").attr('action', '{^遷移先ページ}'); $("form[name='submitForm']").attr('method', 'POST'); $("form[name='submitForm']").submit(); }); }); </script> |
clone関数実行後のHMTL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <form name="submitForm" id="submitForm"> <input type="text" name="field1" value="2015" /> <input type="text" name="field2" value="12" /> <select name="field3"> <option id=""></option> <option id="1">ノートPC</option> <option id="2">デスクトップPC</option> <option id="3">タブレットPC</option> </select> </form> <!-- コピーするHTMLオブジェクト --> <div class="container"> <input type="text" name="field1" value="2015" /> <input type="text" name="field2" value="12" /> <select name="field3"> <option id=""></option> <option id="1" selected="selected">ノートPC</option> <option id="2">デスクトップPC</option> <option id="3">タブレットPC</option> </select> </div> <input type="button" id="regist-button" value="登録" /> |
HTMLオブジェクトはコピーされるもののセレクトボックスのselected属性が引き継がれません。
このまま気が付かずにフォームのサブミット処理が実行されてしまうと、
遷移先のページでセレクトボックスに対応する変数がnull値になってしまいます。
clone関数にオプションを付け加えることで対策できないか調べてみましたが、
どうやら打つ手なしのようです。
仕方ないのでクローン化した後に各コントロールの入力値を引き継ぐように対応させました。
セレクトボックスの入力値を引き継がせるスクリプトを後述
あまり堅実的ではないですが、clone関数だけでは頼りないので
スクリプトコードを以下のように変更して対応させました。
スクリプトコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <script type="text/javascript"> $(document).ready(function() { // 登録ボタン押下時の処理 $("#regist-button").click(function() { // 入力情報をクローンしてformにコピー $("div[class='container']").clone(false) .appendTo("form[name='submitForm']"); // セレクトボックスの入力値の登録(clone関数の不具合対応) var itemArr = []; // 適当に配列オブジェクト宣言 $("div[class='container']).find("select").each(function(i) { // セレクトボックスのname属性を要素に値を格納 itemArr[$(this).attr('name')] = $(this).val(); }); // 取得した連想配列から代入処理 for (var idx in itemArr) { $("form[name='submitForm'] [name='"+idx+"']").val(itemArr[idx]); } // フォームのサブミット処理 $("form[name='submitForm']").attr('action', '{^遷移先ページ}'); $("form[name='submitForm']").attr('method', 'POST'); $("form[name='submitForm']").submit(); }); }); </script> |
上記のソースコードはあくまでサンプルなので、HTMLの構造によっては対応できないものもあります。
適宜ソースを調整して値渡しをする工夫が必要ですね。
あとは同名のname属性のセレクトボックスが存在する場合は対応できないので、その場合もソースの改変が必要です。
jQueryのclone関数の引数true/falseとremove関数の組み合わせに注意
clone関数内の引数でboolean型を指定しますが、この真偽値はtrueならコピー元のイベントハンドラを引き継ぎます。つまり、元のイベントハンドラを参照します。
falseなら引き継ぎは行われません。
さて今回のようにサブミット処理時に特定のデータをクローン化させて
データ送信を行なう場合でも、処理実行後に画面を遷移しないようにすることってありますよね。
formaタグにtarget属性を指定した場合や
サブミットではなくajaxで非同期通信する場合などです。
この時、クローン化させたHTMLオブジェクトを削除しておかないと、
処理を実行した回数分オブジェクトのクローンが生成されておかしなことになります。
なので処理後にremove関数などでコピー先HTMLオブジェクトを削除しておきます。
が、ここで問題があります。
clone関数の引数をtrue指定し、remove関数を実行するとイベントハンドラまで削除されます。
要するに、コピー元のイベントまでも削除されてしまうということです。
例えば、クリックイベントが削除されてしまうなどなど・・・。
イベントハンドラを別々にしたい場合は、clone関数利用時にはfalseを指定しておきましょう。
最新記事 by よっき (全て見る)
- 「圧着」と「圧接」の違い!コネクタを使った効率的な配線作業! - 2019年10月26日
- 夏の暑さ対策は大丈夫?冷却性能抜群のおすすめCPUクーラー!メモリに干渉しない最強の商品を紹介! - 2018年5月1日
- 自作PC弐号機のケースを換装!SilverStone製のミニタワーで冷却性とかっこよさを追求! - 2018年3月11日
スポンサードリンク