HTML5 Drag & Drop API

In diesem Beitrag werden die Grundlagen von Drag-and-drop erläutert.

Ziehbare Inhalte erstellen

In den meisten Browsern sind Textauswahlen, Bilder und Links standardmäßig ziehbar. Wenn du beispielsweise einen Link auf eine Webseite ziehst, siehst du ein kleines Feld mit einem Titel und einer URL, die du in die Adressleiste oder den Desktop ziehen kannst, um eine Verknüpfung zu erstellen oder zum Link zu gehen. Wenn Sie andere Inhaltstypen ziehbar machen möchten, müssen Sie die HTML5 Drag & Drop APIs verwenden.

Wenn Sie ein Objekt ziehbar machen möchten, legen Sie für dieses Element draggable=true fest. Die Drag-Funktion ist praktisch alles möglich, einschließlich Bilder, Dateien, Links, Dateien oder jegliches Markup auf Ihrer Seite.

Mit dem folgenden Beispiel wird eine Benutzeroberfläche zum Neuanordnen von Spalten erstellt, die mit dem CSS-Raster angeordnet wurden. Das grundlegende Markup für die Spalten sieht so aus, wobei das Attribut draggable für jede Spalte auf true festgelegt ist:

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

Hier ist der CSS-Code für die container- und box-Elemente. Das einzige CSS, das mit der Drag-Funktion in Verbindung steht, ist die cursor: move-Property. Der Rest des Codes steuert das Layout und den Stil der Container- und Box-Elemente.

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

Jetzt können Sie die Elemente ziehen, ansonsten passiert aber nichts. Wenn Sie das Verhalten hinzufügen möchten, müssen Sie die JavaScript API verwenden.

Auf Drag-Events warten

Zum Überwachen des Drag-Vorgangs können Sie auf eines der folgenden Ereignisse warten:

Für den Ziehfluss benötigen Sie ein Quellelement (wo das Ziehen beginnt), die Datennutzlast (das gezogene Element) und ein Ziel (ein Bereich zum Auffangen des Drag-Vorgangs). Das Quellelement kann fast jede Art von Element sein. Das Ziel ist die Drop-Zone oder eine Gruppe von Drop-Zonen, die die Daten aufnehmen, die der Nutzer löschen möchte. Nicht alle Elemente können Ziele sein. Ihr Ziel darf beispielsweise kein Bild sein.

Ziehsequenz starten und beenden

Nachdem Sie draggable="true"-Attribute für Ihre Inhalte definiert haben, hängen Sie einen dragstart-Event-Handler an, um die Ziehsequenz für jede Spalte zu starten.

Mit diesem Code wird die Deckkraft der Spalte auf 40% festgelegt, wenn der Nutzer beginnt, sie zu ziehen, und sie auf 100% zurück, wenn das Drag-Ereignis endet.

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

Das Ergebnis kannst du in der folgenden Glitch-Demo sehen. Wenn Sie ein Element ziehen, ändert sich seine Deckkraft. Da das Quellelement das Ereignis dragstart enthält, erhält der Nutzer durch Festlegen von this.style.opacity auf 40% ein visuelles Feedback, dass dieses Element die aktuelle Auswahl ist, die verschoben wird. Wenn Sie das Element löschen, erhält das Quellelement wieder 100% Deckkraft, obwohl Sie das Ablegenverhalten noch nicht definiert haben.

Zusätzliche visuelle Hinweise hinzufügen

Verwenden Sie die Event-Handler dragenter, dragover und dragleave, damit Nutzer besser verstehen, wie sie mit Ihrer Benutzeroberfläche interagieren können. In diesem Beispiel sind die Spalten nicht nur ziehbar, sondern auch Drop-Ziele. Damit der Nutzer dies versteht, wird der Rahmen gestrichelt, wenn er ein gezogenes Element über eine Spalte hält. In Ihrem CSS könnten Sie beispielsweise eine over-Klasse für Elemente erstellen, die Drop-Ziele sind:

.box.over {
  border: 3px dotted #666;
}

Richten Sie dann in Ihrem JavaScript die Event-Handler ein, fügen Sie die Klasse over hinzu, wenn die Spalte über die Spalte gezogen wird, und entfernen Sie sie, wenn das gezogene Element verlassen wird. Im dragend-Handler müssen auch die Klassen am Ende des Ziehens entfernt werden.

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

In diesem Code gibt es einige Punkte, die es zu behandeln gilt:

  • Die Standardaktion für das Ereignis dragover besteht darin, das Attribut dataTransfer.dropEffect auf "none" zu setzen. Die Eigenschaft dropEffect wird weiter unten auf dieser Seite behandelt. Beachten Sie vorerst, dass das drop-Ereignis dadurch nicht ausgelöst wird. Wenn Sie dieses Verhalten überschreiben möchten, rufen Sie e.preventDefault() auf. Außerdem empfiehlt es sich, false im selben Handler zurückzugeben.

  • Der Event-Handler dragenter wird zum Umschalten der over-Klasse anstelle von dragover verwendet. Wenn Sie dragover verwenden, wird das Ereignis wiederholt ausgelöst, während der Nutzer das gezogene Element über eine Spalte hält. Dadurch wird die CSS-Klasse wiederholt umgeschaltet. Der Browser führt dann viele unnötige Rendering-Aufgaben aus, was sich negativ auf die Nutzererfahrung auswirken kann. Es wird dringend empfohlen, Neuzeichnungen so gering wie möglich zu halten. Wenn Sie dragover verwenden müssen, sollten Sie den Event-Listener drosseln oder aufheben.

Loslegen

Fügen Sie einen Event-Listener für das drop-Ereignis hinzu, um das Ablegen zu verarbeiten. Im drop-Handler musst du das Standardverhalten des Browsers für Drops verhindern, bei dem es sich in der Regel um eine lästige Weiterleitung handelt. Rufen Sie dazu e.stopPropagation() auf.

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

Der neue Handler muss zusammen mit den anderen Handlern registriert werden:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

Wenn Sie den Code an dieser Stelle ausführen, wird das Element nicht am neuen Speicherort abgelegt. Dazu verwenden Sie das Objekt DataTransfer.

Das Attribut dataTransfer enthält die bei einer Drag-Aktion gesendeten Daten. dataTransfer wird im dragstart-Ereignis festgelegt und im Drop-Ereignis gelesen oder verarbeitet. Durch Aufrufen von e.dataTransfer.setData(mimeType, dataPayload) können Sie den MIME-Typ und die Datennutzlast des Objekts festlegen.

In diesem Beispiel werden Nutzer die Reihenfolge der Spalten ändern können. Dazu müssen Sie zuerst den HTML-Code des Quellelements speichern, wenn das Ziehen beginnt:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

Im drop-Ereignis verarbeiten Sie den Spalteninhalt, indem Sie den HTML-Code der Quellspalte auf den HTML-Code der Zielspalte setzen, in der Sie die Daten gelöscht haben. Dazu gehört auch, sicherzustellen, dass der Nutzer nicht auf dieselbe Spalte zurückspringt, aus der er gezogen hat.

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

Das Ergebnis können Sie sich in der folgenden Demo ansehen. Dazu benötigen Sie einen Desktop-Browser. Die Drag & Drop-API wird auf Mobilgeräten nicht unterstützt. Ziehen Sie die Spalte A über die Spalte B und lassen Sie sie los. Sie sehen, wie sich die Position ändert:

Weitere Drag-Eigenschaften

Das dataTransfer-Objekt stellt Eigenschaften bereit, um dem Nutzer während des Drag-Vorgangs visuelles Feedback zu geben und zu steuern, wie die einzelnen Drop-Ziele auf einen bestimmten Datentyp reagieren.

  • Mit dataTransfer.effectAllowed wird eingeschränkt, welche Art von Zieh der Nutzer für das Element ausführen kann. Sie wird im Drag-and-drop-Verarbeitungsmodell verwendet, um dropEffect während der Ereignisse dragenter und dragover zu initialisieren. Das Attribut kann die folgenden Werte haben: none, copy, copyLink, copyMove, link, linkMove, move, all und uninitialized.
  • dataTransfer.dropEffect steuert das Feedback, das der Nutzer während der Ereignisse dragenter und dragover erhält. Wenn der Nutzer den Mauszeiger auf ein Zielelement hält, zeigt der Cursor des Browsers an, welche Art von Vorgang stattfindet, z. B. ein Kopieren oder Verschieben. Die Auswirkung kann einen der folgenden Werte annehmen: none, copy, link, move.
  • e.dataTransfer.setDragImage(imgElement, x, y) bedeutet, dass Sie anstelle des standardmäßigen „Ghost Image“-Feedbacks des Browsers ein Drag-Symbol festlegen können.

Datei hochladen

In diesem einfachen Beispiel wird eine Spalte sowohl als Ziehquelle als auch als Ziehziel verwendet. Das kann in einer UI passieren, in der der Nutzer aufgefordert wird, Elemente neu anzuordnen. In einigen Situationen können das Ziehziel und die Quelle unterschiedliche Elementtypen sein, z. B. auf einer Benutzeroberfläche, auf der der Nutzer ein Bild als Hauptbild für ein Produkt auswählen muss, indem er das ausgewählte Bild auf ein Ziel zieht.

Drag-and-drop wird häufig verwendet, damit Nutzer Elemente von ihrem Desktop in eine Anwendung ziehen können. Der Hauptunterschied besteht in Ihrem drop-Handler. Die Daten sind im Attribut dataTransfer.files enthalten, anstatt dataTransfer.getData() für den Zugriff auf die Dateien zu verwenden:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

Weitere Informationen hierzu finden Sie unter Benutzerdefiniertes Drag-and-drop.

Weitere Ressourcen