HTML5 Drag and Drop APIで一覧の並び替えをする
今、自分が個人開発で作っているプロダクトでDnD(ドラッグ&ドロップ)を使いたい。
調べてたらHTML5のAPIを使うと意外と簡単にできることがわかった。
このリンク先に非常にわかりやすくまとまっている。
特別な要件がない限りは、HTML 5のAPIを使うだけでDnDは実現できそうだ。
上記の記事に書かれている通りにやっていくと、DnDが動作することはよくわかるんだけどこのサンプルはDnDした先と元の要素を入れ替えるという代物になっている。
たとえばA,B,Cと要素があったとして、このサンプルでCをドラッグしてAの位置に持っていくと、C,B,Aになる。
でも、期待されているケースとして多いのは入れ替えではなくて並び替えではないか?と思ったし、実際自分が使いたいのも並び替えのほう。
つまりA,B,Cと要素があって、CをドラッグしてAの位置に持っていくと、C,A,Bになるようにしたい。
そこで、上記のサンプルをベースにして並び替えのコードに書き直しをしてみた。
組んでみたサンプルはこちら。
See the Pen html sample by Makoto Ohnami (@zohnami) on CodePen.
オリジナルとの違いは?というとhandleDrop
の中の処理を主に書き換えしています。
function handleDrop(e) {
if (e.stopPropagation) {
e.stopPropagation(); // stops the browser from redirecting.
}
if (dragSrcEl != this) {
// オリジナルはdragSrcElとcurrentTargetを入れ替えしているだけだけど
// ここではcloneNodeしたものをcurrentTargetの後ろに足して
// dragSrcElを削除することで並び替えを実現している
let cloneNode = dragSrcEl.cloneNode(true)
cloneNode.style.opacity = 1.0
e.currentTarget.after(cloneNode)
dragSrcEl.remove()
reset(e)
}
return false;
}
そして並び替えの場合は挿入位置が明確になった方がいいと思うので cssで並び替えのときの演出も少し調整。 挿入位置にバーが表示されるように変更した。
.box.over {
border-bottom: 4px solid #333;
padding-bottom: 20px;
margin-bottom: 20px;
}
もうひとつポイント。DnDをしたときに指定先の要素の下に追加する、ということは何もしないと要素を一番上に持ってくることができない。
そこで、ブランクのdiv要素(DnDできないけど、DnDの先としては存在する)を設置した。
<div class="container">
<div draggable="true"class="box">
<!-- これが一番上に持ってくるという動作を受け入れてくれる -->
</div>
<div draggable="true"class="box">
<div class="box__inner">
A
</div>
</div>
<!-- 以下略 -->
</div>
たとえば、並び替えた情報をバックエンドに送ってデータベースを更新したい、なんてときは並び替える要素にidなんかを忍ばせておいて
<!-- data-idのネーミングは適当 -->
<div draggable="true"class="box" data-id="123">
<div class="box__inner">
A
</div>
</div>
dropイベントの時にgetAttributeでコピー先のidを取得できます。 この情報を使えばバックエンドで適宜順番を更新して、並び替えた情報を永続化できるでしょう。
const towardId = event.currentTarget.getAttribute('data-id')
// このtowardIdの下にドラッグしたものが入る
SPAのアプリなんかで、リストの並び替えに使えるのはこっちのパターンですね。
jsのライブラリを活用しないと厳しいかと思っていたけれど、そんなことなかった。
ライブラリは栄枯盛衰があるし、できれば素のjsとAPIで実現できるのならそれが一番だと思う。
サンプルを動かしていると、要素を上に持っていく並び替えのときにDnDが途中で止まっちゃうようなことがあるんだけど、これなんとかしたい。
下に持っていくパターンは特に問題なし。
もし対処法に気がついた方がいたら、教えてください。
この記事の環境情報
- HTML,CSS,javascript
- コーヒー飲みながら書いた
JavaScriptに関する他の記事を読む
- タグマネージャーでjsのloadイベントを発火させたいとき
- [JavaScriptをわかりやすく]removeメソッドで自身を動的に削除する
- [JavaScriptをわかりやすく]子要素をremoveChildで動的に削除する
- [JavaScriptをわかりやすく]insertAdjacentHTMLでタグを直接追加する
- [JavaScriptをわかりやすく]beforeとafterで指定した要素の前後に動的に要素を追加する
htmlに関する他の記事を読む
- [JavaScript]親要素の表示領域でクリックしているかを判定する方法
- 続・cssの一箇所を変更してテーマ全体を入れ替える
- cssの一箇所を変更してテーマ全体を入れ替えるサンプル
- [CSS]背景色を点滅させるアニメーションサンプル
- クリックしたときにCSSで線を引くサンプル
最近の記事を読む
- Flutterでcontextを使わずにlocale情報を取得する
- Cloud RunでIAPを有効にしようとしてハマったこと
- slimでtype='application/ld+json'のscriptタグを書く
- タグマネージャーでjsのloadイベントを発火させたいとき
- mysqlコマンドを実行してERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)と言われたら