HTMLとJavaScriptでドラッグアンドドロップできるリストを作る

JavaScript

今回は、ドラッグアンドドロップができるリストを作成します。

Drag and Drop Sortable List With Javascript (Simple Example)の記事を参考にしました。

完成形のGIF動画です。

リストをドラッグ&ドロップして順番が入れ替える
リストをドラッグ&ドロップして順番が入れ替える

ファイルの構造はこんな感じにしています。全部同じディレクトリです。

- index.html
- style.css
- script.js

それぞれコードを見ていきましょう。

HTML

まずはHTMLを見ていきましょう。

index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ドラッグ&ドロップできるリスト</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <ul id="list">
      <li>仕事</li>
      <li>すごい仕事</li>
      <li>かなりすごい仕事</li>
      <li>とてもかなりすごい仕事</li>
      <li>めちゃくちゃとてもすごい仕事</li>
    </ul>
    <script src="script.js"></script>
  </body>
</html>

index.htmlの解説

headタグ内で、style.cssを読み込んで、</body>の直前でscript.jsを読み込んでいます。

HTMLエレメントはリストだけです。ulのidをlistとつけています。

リストの順番変更がわかりやすいように、テキストの長さを調整しました。

シンプルな構造ですね。これをCSSで装飾して、JavaScriptで動かします。

CSS

次はCSSです。

style.css

#list {
  list-style-type: none;
  padding: 0;
  margin: 0;
  max-width: 400px;
}

#list li {
  margin: 10px;
  padding: 15px;
  border: 2px solid #dfdfdf;
  border-radius: 10px;
}

#list li.hint {
  border: 2px dashed #dfdfdf;
}

#list li.active {
  border: 2px dashed #ff597b;
}

style.cssの解説

上2つ、#listと#list li に関しては、HTML内に記述してある要素の装飾です。
色や線や余白を調整しています。

下の、li.hintは、ドラッグ状態(ondragstart)のときにドロップできる要素を指します。破線になります。

li.activeは、ドラッグしているときにホバー状態(ondragenter)になる要素を指します。赤っぽい色の破線になります。

CSSも簡単ですね。次はJavaScriptです。

JavaScript

JavaScriptはちょっと説明がいりますね。

HTML内のli要素をループして、それぞれの要素にDragEventを付与していきます。

script.js

const target = document.getElementById("list");
const items = target.getElementsByTagName("li");
let current;

for (let item of items) {
  item.draggable = true;
  item.ondragstart = () => {
    current = item;
    for (let i = 0; i < items.length; i++) {
      if (items[i] !== current) {
        items[i].classList.add("hint");
      }
    }
  };
  item.ondragenter = () => {
    if (item !== current) {
      item.classList.add("active");
    }
  };
  item.ondragleave = () => {
    item.classList.remove("active");
  };
  item.ondragend = () => {
    for (let i = 0; i < items.length; i++) {
      items[i].classList.remove("hint", "active");
    }
  };
  item.ondragover = (ev) => {
    ev.preventDefault();
  };
  item.ondrop = (ev) => {
    ev.preventDefault();
    if (item !== current) {
      let currentPosition = 0;
      let droppedPosition = 0;
      for (let i = 0; i < items.length; i++) {
        if (current === items[i]) {
          currentPosition = i;
        }
        if (item === items[i]) {
          droppedPosition = i;
        }
      }
      if (currentPosition < droppedPosition) {
        item.parentNode.insertBefore(current, item.nextElementSibling);
      } else {
        item.parentNode.insertBefore(current, item);
      }
    }
  };
}

script.jsの解説

まず、HTML要素のid=”list”のulを取得して、さらにその下のliを取得してitemsに格納しています。
変数currentは後で使います。

itemsをループしてそれぞれの<li>に対して、DragEventを設定していきます。
item = <li>ということですね。

ループ内では、最初にdraggableをtrueにして、要素をドラッグできるようにします。

ondragstartの際には、forループを使って、該当のitem以外のクラスに”hint”を加えます。これでその他の<li>の枠が破線になります。

ondragenterは、ドラッグされた要素がそのエリアに入ると実行されるイベントです。
ここではクラスに”active”を加えています。赤い破線になります。

ondragleaveは、ondragenterの逆で、ドラッグされた要素がエリアから出ると、クラスの”active”が除去されます。赤い破線が消えます。

ondragendは、ondragstartの逆で、ドラッグが終わると実行されます。
forループを使って全ての<li>のクラスから、”hint”と”active”を除去します。

ondragoverは、ドラッグされた要素がドロップ先の要素の上に重なると実行されます。
デフォルトではドロップできないようになっているため、preventDefault()を実行します。

ondropは、名前の通り要素がドロップされたときに実行されます。
デフォルトでは、要素によってドロップした際にリンクとして開かれるということがあるらしいのでpreventDefault()でそれを防止します。

if文の中で、itemとcurrentを比較していますが、ここのitemはドロップ先の<li>で、currentはドラッグ中の<li>に対応します。

で、itemとcurrentが異なる場合にforループが実行されます。

forループの中では、ドラッグ中の<li>の順番(currentPosition)とドロップ先の<li>の順番(doroppedPosition)を調べます。

もしドラッグ中の<li>の順番が、ドロップ先の<li>の順番よりも小さかったら、ドロップ先の<li>の次の<li>の前に挿入(insertBefore())します。

逆に、ドラッグ中の<li>の順番が、ドロップ先の<li>の順番よりも大きかったら、ドロップ先の<li>の前に挿入(insertBefore())します。

こうすることできれいにリストの順番を入れ替えることができます。

Codepen

最後にCodepenを埋め込んでおきます。

See the Pen Untitled by tkyytnm (@tkyytnm) on CodePen.

ドラッグ&ドロップは奥が深いですね。機会があれば深く勉強したいです。

コメント

タイトルとURLをコピーしました