今、自分が個人開発で作っているプロダクトでDnD(ドラッグ&ドロップ)を使いたい。

調べてたらHTML5のAPIを使うと意外と簡単にできることがわかった。

このリンク先に非常にわかりやすくまとまっている。

HTML5 Drag and Drop 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
  • コーヒー飲みながら書いた