ウェブプログラミングをしていると、FormをSubmitしてデータをサーバーに送信してデータベースを検索して、その検索結果を表示するなどの操作を良く行う。
その時にチェックボックスのチェック状態、Selectの選択項目、input textの入力文字列などを記憶して復元出来ると便利である。
例えばこんなフォームがあるとする。
文字列 | |
---|---|
チェックボックス | |
セレクト |
上のチェックボックスやセレクトなどを適当に変更して、このページを再読み込みする。
そうすると、再読み込み前の状態に復元できるはずだ。
当記事では、ワテが普段使っているその手法を紹介したい。
まあ、良く忘れるのでワテの備忘録であるが。
では、本題に入ろう。
クライアントサイドとサーバーサイド
今回のように、チェックボックスなどの状態を記憶して復元するにはいろんな手法がある。
サーバーサイドで状態を復元する
FormをSubmitすると、name属性で指定されているパラメータはサーバーサイドに送信される。
それを配列に取り出してhtmlページを生成する時に利用すればチェック状態を復元する事が出来る。
こんな感じか。
<?php $item_arr = $_GET['item']; ?> <input type="checkbox" name="item[]" value="1" <?php if(in_array('1', $item_arr))echo('checked'); ?> />
ワテの場合、PHPは初心者レベルなので間違いがあるかも知れない。
サーバーサイドで復元する手法は、コードがシンプルで行数も少ないのでお勧めだ。
ただし、当然ながらサーバーサイドに送信されていない要素が有れば、その状態は復元する事は出来ない。
クライアントサイドで状態を復元する
クライアントサイドとはブラウザー側の処理だ。
チェック状態の記憶と復元をブラウザー側でやってみる。
その為には、チェックボックスのチェック状態が変化する度に、その状態をパソコンに記憶するのだ。
記憶する手段としては、
- クッキー
- LocalStorage
などが良く使われる。
パソコンにデータを記憶すると言う目的では、クッキーでもLocalStorageでもどちらでも似た様なもんだ。
ワテの場合は、最近はLocalStorageを使っている。
理由は、クッキーだと有効期間を指定する必要があるがLocalStorageならそんなのは必要ないと言う単純な理由だ。
例えばこんなHTMLがあるとする。
<form></form>で囲って、buttonにtype=”submit” を与えれば、サーバーに送信する事も可能だ。
<table> <tr> <th>チェックボックス</th> <td> <label><input type="checkbox" name="item[]" value="1" data-savekey="1" class="save-state1">チェック項目1</label><br /> <label><input type="checkbox" name="item[]" value="2" data-savekey="2" class="save-state1">チェック項目2</label><br /> <label><input type="checkbox" name="item[]" value="3" data-savekey="3" class="save-state1">チェック項目3</label><br /> <label><input type="checkbox" name="item[]" value="4" data-savekey="4" class="save-state1">チェック項目4</label><br /> <label><input type="checkbox" name="item[]" value="5" data-savekey="5" class="save-state1">チェック項目5</label><br /> <label><input type="checkbox" name="item[]" value="6" data-savekey="6" class="save-state1">チェック項目6</label> </td> </tr> </table> <input type="button" value="このページを再読込します" id="button1_ID" />
このHTMLにおいて、六つのチェックボックスのチェック状態が変化するたびにLocalStorageに状態を保管する関数を作成した。
また、ページがloadされた時には、LocalStorageに保管されているデータが有ればそれを使ってチェック状態を復元する機能も入れている。
それがこれだ。
function alertDebug(arg) { //alert(arg); // デバッグ時に有効化すると良い } function save_restore1_checkbox(target_class) { var cbstate; window.addEventListener('load', function () { cbstate = JSON.parse(localStorage['CBState'] || '{}'); alertDebug('cbstate = ' + JSON.stringify(cbstate)); for (var key in cbstate) { // cbstateはobjectで、このようにforループすると var key はobjectのキーが来るのだ。知らなんだ。 alertDebug('key=' + key); var el_lst = document.querySelectorAll('input[data-savekey="' + key + '"].' + target_class); set_checkbox_checked_all(el_lst, true); } var cb = document.getElementsByClassName(target_class); alertDebug('cb = ' + JSON.stringify(cb)); for (var c = 0; c < cb.length; c++) { alertDebug('cb[' + c + ']:name=' + cb[c].name + ', value=' + cb[c].value); cb[c].addEventListener('click', function (evt) { var savekey = this.getAttribute('data-savekey'); alertDebug('click:savekey_value=' + savekey + ', checked=' + this.checked); if (this.checked) { cbstate[savekey] = true; } else if (cbstate[savekey]) { delete cbstate[savekey]; } localStorage['CBState'] = JSON.stringify(cbstate); }); } }); function set_checkbox_checked_all(el_lst, checked) { for (var c = 0; c < el_lst.length; c++) { var el = el_lst[c]; alertDebug('el=' + JSON.stringify(el) + ' ,el.name=' + el.name); if (el) { alertDebug('el.checked=' + el.checked); el.checked = checked; } } } } save_restore1_checkbox('save-state1');
処理内容は、そんなには複雑では無いのでJavaScriptが分る人なら説明しなくても処理の流れは分ると思う。
チェックボックスのチェック状態が変化した場合にイベントハンドラが実行される。
もしそのチェックボックスがチェックされた場合には、それをLocalStorageに書き込む。
書き込むデータはオブジェクトをJSON文字列化したものにした。
そのJSONの中身は、チェックボックスに独自に追加したdata-savekeyと言う属性の値をキーにして、値は true を書き込んでいる。
checked=true のデータのみを保存して false の場合は保存しないので、保存する値は true で無くても良いのだが、取り敢えず true を保存している。
チェックボックスの状態を記憶・復元する手法の使い方
使い方としては、以下のように引数でクラスを指定して実行する。
save_restore1_checkbox('save-state1');
このクラス ‘save-state1’ を持つチェックボックスを全部探して、それぞれのチェックボックスにイベントハンドラを割り当てている。
そのハンドラ内では、チェック状態が変化する度に、LocalStorageを読み書きしている。
LocalStorageにJSONデータ形式でデータを保管する事は上で説明したが、そのJSONオブジェクトのキーは data-savekey=”1″ などと言う属性で与える事にしている。もちろんキー文字列は “1” などでは無くて、任意の文字列を利用できる。
まあ、この辺りは好き好きなので、data-savekeyなどと言う独自なdata-attributeは使わずに value=”1″ などの値をキーに用いても良いだろう。
実は当初はそのようにしていたのだが、何らかの理由でvalueの値を変更した場合には、LocalStorageのキーが変わってしまうのでデータが読み出せなくなる。
なので、LocalStorageへのデータ保管用に独自の属性data-savekeyを定義したのだ。
LocalStorageの中身を確認する
このようにして作成したHTMLをブラウザーで表示する。
そして、表示されたチェックボックスのチェック状態を変化させてみる。
ブラウザーのF12で開発者モードに入ると下図のようにLocalStorageの中身を確認する事が出来る。
ブラウザーで表示されているチェックボックスのチェック状態を変化させれば、LocalStorageのデータが動的に変化する様子を確認出来るのでデバッグ時には役立つ。
上図に於いて、JSONオブジェクトの中の “1”: の部分が、 data-savekey=”1″ で指定したキーである。
分かり易くするために数字 “1”, “2” などを用いているが、先ほども説明したようにこのキーは、他のキーと重複しなければどんな文字列でも良い。
Selectやinput Textも同様の手法で記憶・復元可能とした
Select/Optionで作成したDropDownメニューやinput textで作成した文字列入力欄に対しても、類似の関数を作成してLocalStorageに状態やデータを記憶し、復元するようにした。
それらの全ソースコードを紹介しよう。
全HTMLコード
jQueryを使わずにJavaScriptのみで記述してみた。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>チェックボックスなどの状態を記憶復元する</title> </head> <body> <div id="myDivID"> <table> <tr> <th>文字列</th> <td> <input type="search" name="s" data-savekey="1" class="save-state2" placeholder="キーワード" value="初期値" /> </td> </tr> <tr> <th>チェックボックス</th> <td> <label><input type="checkbox" name="item[]" value="1" data-savekey="1" class="save-state1">チェック項目1</label><br /> <label><input type="checkbox" name="item[]" value="2" data-savekey="2" class="save-state1">チェック項目2</label><br /> <label><input type="checkbox" name="item[]" value="3" data-savekey="3" class="save-state1">チェック項目3</label><br /> <label><input type="checkbox" name="item[]" value="4" data-savekey="4" class="save-state1">チェック項目4</label><br /> <label><input type="checkbox" name="item[]" value="5" data-savekey="5" class="save-state1">チェック項目5</label><br /> <label><input type="checkbox" name="item[]" value="6" data-savekey="6" class="save-state1">チェック項目6</label> </td> </tr> <tr> <th>セレクト1</th> <td> <select name="name_select[]" data-savekey="1" class="save-state3"> <option value="選択値1">選択項目1</option> <option value="選択値2">選択項目2</option> <option value="選択値3">選択項目3</option> <option value="選択値4">選択項目4</option> <option value="選択値5">選択項目5</option> </select> </td> </tr> <tr> <th>セレクト2</th> <td> <select name="name_select[]" data-savekey="2" class="save-state3"> <option value="選択値11">選択項目11</option> <option value="選択値12">選択項目12</option> <option value="選択値13">選択項目13</option> <option value="選択値14">選択項目14</option> <option value="選択値15">選択項目15</option> </select> </td> </tr> </table> <input type="button" value="このページを再読込します" id="button1_ID" /> </div> <style> #myDivID > table { border-collapse: separate; border: 1px solid gray; } #myDivID > table th, #myDivID > table td { border: 1px solid gray; } </style> <script> document.getElementById('button1_ID').onclick = function () { window.location.reload(); }; function alertDebug(arg) { //alert(arg); // デバッグ時に有効化すると良い } function save_restore1_checkbox(target_class) { var cbstate; window.addEventListener('load', function () { cbstate = JSON.parse(localStorage['CBState'] || '{}'); alertDebug('cbstate = ' + JSON.stringify(cbstate)); for (var key in cbstate) { // cbstateはobjectで、このようにforループすると var key はobjectのキーが来るのだ。知らなんだ。 alertDebug('key=' + key); var el_lst = document.querySelectorAll('input[data-savekey="' + key + '"].' + target_class); set_checkbox_checked_all(el_lst, true); } var cb = document.getElementsByClassName(target_class); alertDebug('cb = ' + JSON.stringify(cb)); for (var c = 0; c < cb.length; c++) { alertDebug('cb[' + c + ']:name=' + cb[c].name + ', value=' + cb[c].value); cb[c].addEventListener('click', function (evt) { var savekey = this.getAttribute('data-savekey'); alertDebug('click:savekey_value=' + savekey + ', checked=' + this.checked); if (this.checked) { cbstate[savekey] = true; } else if (cbstate[savekey]) { delete cbstate[savekey]; } localStorage['CBState'] = JSON.stringify(cbstate); }); } }); function set_checkbox_checked_all(el_lst, checked) { for (var c = 0; c < el_lst.length; c++) { var el = el_lst[c]; alertDebug('el=' + JSON.stringify(el) + ' ,el.name=' + el.name); if (el) { alertDebug('el.checked=' + el.checked); el.checked = checked; } } } } save_restore1_checkbox('save-state1'); function save_restore2_inputtext(target_class) { var tbstate; window.addEventListener('load', function () { tbstate = JSON.parse(localStorage['TBState'] || '{}'); alertDebug('tbstate = ' + JSON.stringify(tbstate)); for (var key in tbstate) { var value = tbstate[key]; alertDebug('key=' + key + ' ,value=' + value); var el_lst = document.querySelectorAll('input[data-savekey="' + key + '"].' + target_class); set_textbox_string_all(el_lst, value); } var tb = document.getElementsByClassName(target_class); //'save-state2'); alertDebug('tb = ' + JSON.stringify(tb)); for (var c = 0; c < tb.length; c++) { alertDebug('tb[' + c + ']:name=' + tb[c].name + ', value=' + tb[c].value); tb[c].addEventListener('input', function (evt) { alertDebug('input:value=' + this.value); var savekey = this.getAttribute('data-savekey'); tbstate[savekey] = this.value; localStorage['TBState'] = JSON.stringify(tbstate); }); } }); function set_textbox_string_all(el_lst, value) { for (var c = 0; c < el_lst.length; c++) { var el = el_lst[c]; //alertDebug('el=' + JSON.stringify(el) + ' ,el.name=' + el.name); if (el) { //alertDebug('el.checked=' + el.checked); el.value = value; } } } } save_restore2_inputtext('save-state2'); function save_restore3_select(target_class) { var ddstate; window.addEventListener('load', function () { ddstate = JSON.parse(localStorage['DDState'] || '{}'); alertDebug('ddstate = ' + JSON.stringify(ddstate)); for (var key in ddstate) { var value = ddstate[key]; // =selectindex値 alertDebug('key=' + key + ' ,value=' + value); var el_lst = document.querySelectorAll('select[data-savekey="' + key + '"].' + target_class); set_select_selectindex_all(el_lst, value); } var dd = document.getElementsByClassName(target_class); alertDebug('dd = ' + JSON.stringify(dd)); for (var c = 0; c < dd.length; c++) { alertDebug('dd[' + c + ']:name=' + dd[c].name + ', value=' + dd[c].value); dd[c].addEventListener('change', function (evt) { alertDebug('input:value=' + this.value); var savekey = this.getAttribute('data-savekey'); var selectElement = evt.target; var selectindex = selectElement.selectedIndex; alertDebug('selectindex=' + selectindex); ddstate[savekey] = selectindex; localStorage['DDState'] = JSON.stringify(ddstate); }); } }); function set_select_selectindex_all(el_lst, selectindex) { for (var c = 0; c < el_lst.length; c++) { var el = el_lst[c]; //alertDebug('el=' + JSON.stringify(el) + ' ,el.name=' + el.name); if (el) { //alertDebug('el.checked=' + el.checked); el.selectedIndex = selectindex; } } } } save_restore3_select('save-state3'); </script> </body> </html>
まあこのHTMLソースをファイルに保管してブラウザーで表示すれば上手く動くと思う。
変数名がヘンテコなので分かり辛いかもしれない。
CheckboxはCBと略している。
Select/Optionで作成するドロップダウンリストはDDと略している。
input textはTextBoxの頭文字でTBと略している。
JavaScriptなどのウェブプログラミングの世界の用語と、C#プログラミングの両方の用語が混じってしまったのだ。
上のHTMLコードには制限事項が幾つかある。
- Chrome, Firefox, Edgeでは動作確認済
- Internet Explorer 11では動かなかった
- クライアントサイドでやっているのでページが表示されて最後に状態を復元するので、もたもたした感じがする(その点、サーバーサイドなら表示する時点で状態が復元されているので素早い)
などかな。
複数のチェックボックスグループが有る場合
複数のチェックボックスグループが有る場合には、それら全てのチェックボックスに同じクラス名を与えても良い。
こんな感じ。
save_restore1_checkbox('save-state1');
各チェックボックスに与えるdata-savekeyは重複しない文字列を与えれば良い。
あるいは、チェックボックスグループごとにクラス名を変えても良い。
こんな感じ。
save_restore1_checkbox('save-state10'); save_restore1_checkbox('save-state20');
この場合には、同一クラスに属するチェックボックス間で、data-savekeyが重複せずに一意で有れば良いように作りたかったのだが、未完成だ。
なので、現状では、どのクラスに属するチェックボックスであっても、data-savekeyは他のチェックボックスと同じ値を指定してはいけない。
Select/Optionやinput textの場合も同様である。
まとめ
自称、PHPの初心者でありJavaScriptの達人(嘘)のワテが、ブラウザーで表示したチェックボックスやドロップダウンメニューの選択状態を復元する手法を紹介した。
確か、ここで紹介したコードは、ネット検索していろんなサイトにある手法を比較して、幾つかの良さそうなコードをミックスして自分なりにカスタマイズしたやつだと思う。
一応問題なく動いているのでワテは良く使っているのだが、バグがあるかも知れないのでご注意下さい。
ウェブプログラミングのスクールに通う
プログラミングを学びたい人にお勧めのプログラミングスクールはこれだ。
多数のオンライン学習講座がある
教室又はオンラインで学習できる
DMM WEBCAMPの特徴としては、未経験や初心者の方でも3ヶ月という短期間で、高いエンジニアスキルを身に付けることができる転職・就職保証型プログラミングスクールだ。
受講完了後、3ヶ月以内に転職・就職できない場合は全額返金しています!とのキャッチフレーズなのだ。ほんまかいな!?
この三社のどれが良いかは、コース内容や受講料などで比較検討して頂きたい。
なお、ワレコのサイトで見たと言っても割引などは無い事は言うまでも無いが、試しに言ってみて、どんな反応があったのか教えて頂けると嬉しい。
コメント