移動式マルチプルフォーム
デモページ
htmlの標準マルチプルフォームは、選択したものに色が付く仕様ですが、数が多くなってくると選択しているものがわかり難いということや、並び順の変更がしにくいという難点があります。そういった条件も込みで再現するような選択要素移動式のフォームをご紹介します。
以下サンプルコードです。
<!-- head -->
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"></script>
<script
src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
crossorigin="anonymous"></script>
<style>
form{
max-width: 300px;
margin-bottom: 16px;
}
.flexbox{
display: flex;
margin-bottom: 8px;
}
.box{
margin: 0 8px;
}
.select-box{
width: 150px;
height: 200px;
overflow-y: scroll;
background-color: #DDD;
padding: 4px;
}
.item{
color: #FFF;
background-color: #888;
cursor: pointer;
}
.item:not(:last-child){
margin-bottom: 4px;
}
.result{
padding: 4px;
}
</style>
<!-- body -->
<form action="" method="post">
<div class="flexbox">
<div class="box">
<span>未選択</span>
<div class="select-box">
<div class="item" data-val="item1">
アイテム1
</div>
<div class="item" data-val="item2">
アイテム2
</div>
<div class="item" data-val="item3">
アイテム3
</div>
</div>
</div>
<div class="box">
<span>選択済み</span>
<div class="select-box selected">
</div>
</div>
</div>
<input type="submit" value="送信">
</form>
<div class="result">
<div class="box">
<span>送信結果</span>
<pre><?php var_export($_POST); ?></pre>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$( ".select-box" ).sortable({
connectWith: ".select-box"
})
.bind("sortremove", function(event, ui) {
var obj = ui.item;
if (obj.parent(".select-box").hasClass("selected")) {
// 選択済みエリアに移動してきたらinputを付与
var val = obj.data("val");
obj.append(
'<input type="hidden" name="select[]" value="' + val + '">'
);
} else {
// 未選択テーブルに移動してきたらinputを削除
obj.find("input").remove();
}
});
});
</script>
jQueryとUIの読み込み
まずは必要ライブラリを読み込みます。今回はjQuery UIのsortableを使用するので、jQueryとjQuery UIが必要です。CDNでの読み込み方法は以下の通りです。
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
ブロック要素を使った構成
まずはHTML部分から確認していきます。今回はdivを使って擬似的にマルチプル形式のフォームを作成しています。
まずはデータを飛ばすために、全体をform要素で囲っていますが、送信ボタンも含めて必要なエリアだけが囲えていれば問題ありません。
マルチプル部分で重要なポイントは以下boxの下層部分です。
<div class="box">
<span>未選択</span>
<div class="select-box">
<div class="item" data-val="item1">
アイテム1
</div>
<div class="item" data-val="item2">
アイテム2
</div>
<div class="item" data-val="item3">
アイテム3
</div>
</div>
</div>
<div class="box">
<span>選択済み</span>
<div class="select-box selected">
</div>
</div>
sortableで移動させる両エリアを判定するために、select-boxというクラスを指定しています。これはライブラリに依存している名称ではないので、自由に決めてもらって構いません。
選択済みボックスには、それを判別できるように追加でselectedというクラスを付与しています。こちらも未選択との違いを判別できるのであれば名称は自由です。
次に、itemについてです。これがselectボックスでのoptionの代わりの要素です。itemに付与しているデータ属性(data-val)は、本来optionに付与するvalueの代わりに使用しています。今回はアイテム1、2、3と準備したので、それぞれにitem1、2、3と命名しました。
最後に送信ボタンを準備すれば、HTML部分は完了です。
multipleを使用しない理由
selectのmultipleを使っても、左右間の移動は可能です。以下のサイトでその方法が紹介されています。
※他にも実用的な多くサンプルが紹介されているので、ぜひ参考にしてください
並び順を重視しないのであれば、multipleフォームが使用できるため、データを送信するという点においてはsortableもつよう実装は簡単です。しかし、フレキシブルに送信データを決めることができないのが欠点です。
また、行としてoptionを選択するため、マルチプルの場合は1行に複数要素を含めることが出来ません。div要素等で生成すれば、1行にid、name、チェックの有無などを含めることが可能です。
tableを使用しない理由
tableを使えば、行づくりはより簡単に実装することが可能です。もし1行に複数要素を含めるとしても、より簡単に実装でき向いていると言えます。
しかしtableで作成すると、選択済みボックスの方に問題が残ります。行がなければ高さが0になってしまうため、移動エリアが無くなってしまうのです。そうならないため、高さの指定ができるブロック要素であるdivを採用しました。
動的なinput要素
div要素で構成している以上、データを送信するためにはinput要素を準備しなければなりません。しかし、最初からinput要素をアイテム内に配置してしまえば、未選択データもPOSTすることになります。そうならないためにも、sortableで移動した先が選択済みかどうかを判定して、input hiddenを追加します。
それでは、sortableの内容と合わせて確認してみましょう。
$( ".select-box" ).sortable({
connectWith: ".select-box"
})
まずは、sortableの宣言です。select-boxのクラスに対してメソッドをつなげています。sortableのオプションには、connectWith指定しています。ここにselect-boxを宣言することで、全てのselect-box内で要素の移動ができるようになっています。
次に要素移動時の処理をsortableにつなげます。
.bind("sortremove", function(event, ui) {
var obj = ui.item;
if (obj.parent(".select-box").hasClass("selected")) {
// 選択済みエリアに移動してきたらinputを付与
var val = obj.data("val");
obj.append(
'<input type="hidden" name="select[]" value="' + val + '">'
);
} else {
// 未選択テーブルに移動してきたらinputを削除
obj.find("input").remove();
}
});
そのままbindのメソッドをつなげて、sortremoveでコールバック関数を指定しています。この関数内に移動後の処理を記述していきます。
ui.itemには移動してきたアイテム(class=”item”)が該当します。移動後、そのアイテムが未選択エリアにいるのか、選択済みエリアにいるかを確認するため、条件分岐でselect-boxの要素にselectedクラスが含まれているかをhasClass(“selected”)でチェックします。
もし、selectedのエリアに移動してきたのであれば、input要素を付与します。
// 選択済みエリアに移動してきたらinputを付与
var val = obj.data("val");
obj.append(
'<input type="hidden" name="select[]" value="' + val + '">'
);
ここで使用するのが、data-valです。これをdata(“val”)で取得して、アイテム要素の後ろにappendでhiddenで追加します。これで、選択済みエリアに移動すればinput要素が付与されているため、データを送信することができます。
もし一度選択済みエリアへ移動して、未選択エリアに戻ってきた場合はinput要素は不要です。そのため、条件分岐でのelse内にinput要素を取り除く記述を追加します。
// 未選択テーブルに移動してきたらinputを削除
obj.find("input").remove();
これで、左右移動型疑似マルチプルフォームが完成です。
配列での送信
最後に送信データの形式についてです。
疑似ではありますが、multipleで送られてきたデータがバラバラになっていると処理をしづらくなります。そうならないためにも、input要素は配列として受け渡します。
name="select[]"
こうすることで、POSTすればselect内に選択済みの要素が上から順番に格納されます。
# 送信結果
array (
'select' =>
array (
0 => 'item2',
1 => 'item1',
),
)
もし1行に複数要素を含めて送信する場合も、配列として管理するようにしましょう。そうすれば受け取り側の処理も簡単になります。
まとめ
いかがだったでしょうか。
今回はjQuery UIのsortableを使ったフレキシブルな「移動式マルチプルフォームの作り方」をご紹介しました。
複雑なデータ登録をする際には非常に役立つ方法ですので、システム要素の強いフォームや、直感的で使いやすいフォームを実装したいと考えている方は、ぜひ参考にしてくださいね。