Drag'n'Drop-API: Anwendungsbeispiel

Guten Tag, Freunde!



In diesem Tutorial sehen wir uns den integrierten Drag & Drop-Mechanismus auf der Seite an.



Aus Gründen der Fairness sollte beachtet werden, dass dieser Mechanismus mithilfe von Mausereignissen implementiert werden kann, wie Ilya Kantor in seinem Lehrbuch zeigt. Wir werden jedoch native Tools verwenden, die auf der Spezifikation basieren .



Technischer Support:







Vorschau:







Unsere Aufgabe lautet wie folgt: eine Liste von Aufgaben zu implementieren, die aus drei Spalten besteht: alle Aufgaben, laufende Aufgaben, erledigte Aufgaben. Natürlich muss die Anwendung die Möglichkeit bieten, Aufgaben hinzuzufügen und zu entfernen. Außerdem sollte die Möglichkeit einer willkürlichen Anordnung von Aufgaben bereitgestellt werden. Dies ist einer der interessantesten Teile des Tutorials: Verfolgen Sie das Objekt unter dem gezogenen Objekt und legen Sie fest, wo das gezogene Objekt über oder unter dem verfolgten Objekt positioniert werden soll. Bootstrap



wird für das Styling verwendet . Wenn Sie interessiert sind, folgen Sie mir bitte.







Markup:



<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>


Hier haben wir einen Container mit einem Feld zur Eingabe des Textes einer Aufgabe und einer Schaltfläche zum Hinzufügen zur Liste (Eingabegruppe) sowie drei Containerspalten (Listengruppe) für alle Aufgaben (Aufgabenliste), Aufgaben in Bearbeitung (in -fortschrittsliste) und erledigte Aufgaben (abgeschlossene Liste). Die "Daten" -Attribute sollen das Styling und die Steuerung trennen: Klassen - für das Styling, Daten - für die Verwaltung.



Stile:



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;
}



Die Klassen "In Bearbeitung" und "Abgeschlossen" dienen als Indikatoren dafür, dass sich die Aufgabe in der entsprechenden Spalte befindet. Die "Drop" -Klasse soll die Aufgabe visualisieren, die die Drop-Zone trifft.



Beachten Sie, dass wir nicht alle Drag & Drop-Ereignisse verwenden, sondern die meisten der Hauptereignisse, bevor Sie mit dem Skript fortfahren.



Wir definieren den Hauptcontainer, in dem die Suche nach Elementen durchgeführt und an den die Ereignisverarbeitung delegiert wird:



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


Wir implementieren das Hinzufügen und Löschen von Aufgaben durch Verarbeiten eines Klicks:



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();
    }
  }
});


Gehen wir direkt zum Ziehen.



Zunächst implementieren wir das Betreten der "Wurf" -Zone und verlassen sie, indem wir die entsprechende Klasse hinzufügen / entfernen:



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");
  }
});


Als nächstes verarbeiten wir den Start des Ziehens:



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);
  }
});


Jetzt müssen wir irgendwie das Element unter dem gezogenen verfolgen. Dies ist notwendig, um Aufgaben in der Liste willkürlich anzuordnen, d.h. Tauschen Sie Aufgaben in einer Spalte an bestimmten Stellen aus. Bei der Behandlung des Ereignisses "mousemove" wird hierfür die Methode "elementFromPoint (x, y)" verwendet. Das Schöne an dieser Schnittstelle ist, dass wir zur Bestimmung des "zugrunde liegenden" Elements nur das "Dragover" -Ereignis behandeln müssen:



//     "" 
let elemBelow = "";

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

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


Schließlich behandeln wir das "drop" -Ereignis:



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";
    }
  }
});


Das ist alles. Wie Sie sehen können, nichts kompliziertes. Aber welche Möglichkeiten gibt es, um der Seite Interaktivität hinzuzufügen? Es bleibt abzuwarten, bis mobile Browser diese Technologie implementieren, und alle werden glücklich sein.



Ich hoffe, Sie haben etwas Interessantes für sich gefunden. Vielen Dank für Ihre Aufmerksamkeit und einen schönen Tag.



All Articles