Drag'n'Drop API:使用例

良い一日、友達!



このチュートリアルでは、ページに組み込まれているドラッグアンドドロップメカニズムについて説明します。



公平を期すために、このメカニズムは、Ilya Kantor教科書示しているように、マウスイベントを使用して実装できますが、仕様に基づいたネイティブツールを使用することに注意してください



テクノロジーサポート:







プレビュー:







私たちのタスクは次のとおりです。すべてのタスク、実行中のタスク、完了したタスクの3つの列で構成されるタスクのリストを実装します。もちろん、アプリケーションはタスクを追加および削除する機能を提供する必要があります。その上、タスクの任意の配置の可能性が提供されるべきです。これは、チュートリアルのより興味深い部分の1つです。ドラッグされたアイテムの下のアイテムを追跡し、ドラッグされたアイテムを追跡されたアイテムの上または下に配置する場所を決定します。ブートストラップ



はスタイリングに使用されます。 興味のある方はフォローしてください。







マークアップ:



<head>
    <!-- Bootstrap CSS -->
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
      integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
      crossorigin="anonymous"
    />
    <!-- custom CSS -->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body class="container">
    <h1>Drag & Drop Example</h1>
    <main class="row">
      <div class="input-group">
        <div class="input-group-prepend">
          <span class="input-group-text">Enter new todo: </span>
        </div>
        <input
          type="text"
          class="form-control"
          placeholder="todo4"
          data-name="todo-input"
        />
        <div class="input-group-append">
          <button class="btn btn-success" data-name="add-btn">Add</button>
        </div>
      </div>

      <div class="col-4">
        <h3>Todos</h3>
        <ul class="list-group" data-name="todos-list">
          <li class="list-group-item" data-id="1" draggable="true">
            <p>todo1</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
          <li class="list-group-item" data-id="2" draggable="true">
            <p>todo2</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
          <li class="list-group-item" data-id="3" draggable="true">
            <p>todo3</p>
            <button
              class="btn btn-outline-danger btn-sm"
              data-name="remove-btn"
            >
              X
            </button>
          </li>
        </ul>
      </div>

      <div class="col-4">
        <h3>In Progress</h3>
        <ul class="list-group" data-name="in-progress-list"></ul>
      </div>

      <div class="col-4">
        <h3>Completed</h3>
        <ul class="list-group" data-name="completed-list"></ul>
      </div>
    </main>

    <!-- custom JS -->
    <script src="script.js"></script>
</body>


ここには、タスクのテキストを入力するためのフィールドとそれをリストに追加するためのボタン(input-group)を備えたコンテナーと、すべてのタスク(todos-list)、進行中のタスク(in -progress-list)および完了したタスク(completed-list)。 「データ」属性に関しては、スタイリングと制御を分離することを目的としています。クラス(スタイリング用、データ)は管理用です。



スタイル:



body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #222;
}

main {
  max-width: 600px;
}

.input-group {
  margin: 1rem;
}

.list-group {
  min-height: 100px;
  height: 100%;
}

.list-group-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

div + div {
  border-right: 1px dotted #222;
}

h3 {
  text-align: center;
}

p {
  margin: 0;
}

.completed p {
  text-decoration: line-through;
}

.in-progress p {
  border-bottom: 1px dashed #222;
}

.drop {
  background: linear-gradient(#eee, transparent);
  border-radius: 4px;
}



「進行中」および「完了」クラスは、タスクが対応する列にあることを示すインジケーターとして機能します。「ドロップ」クラスは、ドロップゾーンに到達するタスクを視覚化するように設計されています。



スクリプトに進む前に、すべてのドラッグアンドドロップイベントを使用するのではなく、ほとんどの主要なイベントを使用することに注意してください。



要素の検索が実行され、イベント処理が委任されるメインコンテナを定義します。



const main = document.querySelector("main");


クリックを処理することで、タスクの追加と削除を実装します。



main.addEventListener("click", (e) => {
  //     
  if (e.target.tagName === "BUTTON") {
    //      "data-name"
    const { name } = e.target.dataset;
    //         
    if (name === "add-btn") {
      //      
      const todoInput = main.querySelector('[data-name="todo-input"]');
      //     
      if (todoInput.value.trim() !== "") {
        //   
        const value = todoInput.value;
        //   
        const template = `
        <li class="list-group-item" draggable="true" data-id="${Date.now()}">
          <p>${value}</p>
          <button class="btn btn-outline-danger btn-sm" data-name="remove-btn">X</button>
        </li>
        `;
        //   
        const todosList = main.querySelector('[data-name="todos-list"]');
        //     
        todosList.insertAdjacentHTML("beforeend", template);
        //      
        todoInput.value = "";
      }
    //       
    } else if (name === "remove-btn") {
      //   
      e.target.parentElement.remove();
    }
  }
});


直接ドラッグに行きましょう。



まず、適切なクラスを追加/削除して、「スロー」ゾーンに入り、そこを離れることを実装します。



main.addEventListener("dragenter", (e) => {
  //    
  if (e.target.classList.contains("list-group")) {
    e.target.classList.add("drop");
  }
});

main.addEventListener("dragleave", (e) => {
  if (e.target.classList.contains("drop")) {
    e.target.classList.remove("drop");
  }
});


次に、ドラッグの開始を処理します。



main.addEventListener("dragstart", (e) => {
  //    
  if (e.target.classList.contains("list-group-item")) {
    //      "dataTransfer"    ;
    // dataTransfer    HTML - text/html,
    //         
    e.dataTransfer.setData("text/plain", e.target.dataset.id);
  }
});


次に、ドラッグされた要素の下にある要素を何らかの方法で追跡する必要があります。これは、リスト内のタスクを任意に配置するために必要です。列内のタスクを場所によって交換します。「mousemove」イベントを処理する場合、「elementFromPoint(x、y)」メソッドがこれに使用されます。このインターフェイスの利点は、「基になる」要素を決定するために、「ドラッグオーバー」イベントを処理する必要があることです。



//     "" 
let elemBelow = "";

main.addEventListener("dragover", (e) => {
  //    ;
  //      
  e.preventDefault();

  //     ;
  //   
  elemBelow = e.target;
});


最後に、「ドロップ」イベントを処理します。



main.addEventListener("drop", (e) => {
  //     ,   dataTransfer
  const todo = main.querySelector(
    `[data-id="${e.dataTransfer.getData("text/plain")}"]`
  );

  //   ,     -   
  if (elemBelow === todo) {
    return;
  }

  //      , ,     
  if (elemBelow.tagName === "P" || elemBelow.tagName === "BUTTON") {
    elemBelow = elemBelow.parentElement;
  }

  //      ,     
  if (elemBelow.classList.contains("list-group-item")) {
    //   ,    :
    //    ;
    //       
    //       (  )
    //  
    const center =
      elemBelow.getBoundingClientRect().y +
      elemBelow.getBoundingClientRect().height / 2;
    //     
    // ,       
    // ,  
    if (e.clientY > center) {
      if (elemBelow.nextElementSibling !== null) {
        elemBelow = elemBelow.nextElementSibling;
      } else {
        return;
      }
    }

    elemBelow.parentElement.insertBefore(todo, elemBelow);
    //       
    //  ,     
    todo.className = elemBelow.className;
  }

  //    
  if (e.target.classList.contains("list-group")) {
    //      
    //        "" 
    e.target.append(todo);

    //     ""
    if (e.target.classList.contains("drop")) {
      e.target.classList.remove("drop");
    }

    //       ,    
    const { name } = e.target.dataset;

    if (name === "completed-list") {
      if (todo.classList.contains("in-progress")) {
        todo.classList.remove("in-progress");
      }
      todo.classList.add("completed");
    } else if (name === "in-progress-list") {
      if (todo.classList.contains("completed")) {
        todo.classList.remove("completed");
      }
      todo.classList.add("in-progress");
    } else {
      todo.className = "list-group-item";
    }
  }
});


それで全部です。ご覧のとおり、複雑なことは何もありません。しかし、ページにインタラクティブ性を追加するための可能性は何ですか。モバイルブラウザがこのテクノロジーを実装するまで待つ必要があり、誰もが満足するでしょう。



何か面白いものを見つけていただければ幸いです。ご清聴ありがとうございました。良い一日を。



All Articles