HTML5 Drag & Drop API

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

In den meisten Browsern sind Textauswahl, 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 einfügen kannst, um eine Verknüpfung zu erstellen oder zum Link zu navigieren. Wenn Sie andere Arten von Inhalten per Drag-and-drop verschieben möchten, müssen Sie die HTML5-Drag-and-drop-APIs verwenden.

Wenn Sie ein Objekt ziehbar machen möchten, legen Sie draggable=true für dieses Element fest. Fast alles kann per Drag-and-drop verschoben werden, einschließlich Bildern, Dateien, Links oder Markups auf Ihrer Seite.

Im folgenden Beispiel wird eine Benutzeroberfläche erstellt, mit der Spalten neu angeordnet werden können, die mit CSS-Grid angeordnet wurden. Das grundlegende Markup für die Spalten sieht so aus, wobei das Attribut draggable für jede Spalte auf true gesetzt 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 das CSS für die Container- und Box-Elemente. Der einzige CSS-Code für das Ziehfeature ist das Attribut cursor: move. Der Rest des Codes steuert das Layout und die Formatierung 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;
}

Nun können Sie die Elemente ziehen, aber sonst passiert nichts. Sie müssen die JavaScript API verwenden, um ein Verhalten hinzuzufügen.

Auf Drag-Events warten

Sie können eines der folgenden Ereignisse beobachten, um den Ziehvorgang zu überwachen:

Für die Drag-and-drop-Funktion benötigen Sie ein Quellelement (wo das Ziehen beginnt), die Datennutzlast (das Objekt, das gezogen wird) und ein Ziel (einen Bereich, in den das Objekt abgelegt werden soll). Das Quellelement kann fast jede Art von Element sein. Das Ziel ist die Dropzone oder Gruppe von Dropzones, in die der Nutzer die Daten ablegen möchte. Nicht alle Elemente können Ziele sein. Ihr Ziel kann beispielsweise kein Bild sein.

Ziehfolge starten und beenden

Nachdem du draggable="true"-Attribute für deinen Inhalt definiert hast, musst du einen dragstart-Event-Handler anhängen, um die Ziehsequenz für jede Spalte zu starten.

Mit diesem Code wird die Deckkraft der Spalte auf 40 % gesetzt, wenn der Nutzer sie zu ziehen beginnt, und dann wieder auf 100 %, wenn das Ziehen beendet wird.

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 ist in der folgenden Glitch-Demo zu sehen. Wenn Sie ein Element ziehen, ändert sich seine Deckkraft. Da das Quellelement das Ereignis dragstart enthält, erhalten Nutzer durch die Einstellung von this.style.opacity auf 40% visuelles Feedback, dass dieses Element die aktuell verschobene Auswahl ist. Wenn Sie das Element entfernen, wird für das Quellelement wieder eine Deckkraft von 100% festgelegt, auch wenn Sie das Drop-Verhalten noch nicht definiert haben.

Zusätzliche visuelle Hinweise hinzufügen

Verwenden Sie die Event-Handler dragenter, dragover und dragleave, um Nutzern zu verdeutlichen, wie sie mit Ihrer Benutzeroberfläche interagieren können. In diesem Beispiel sind die Spalten nicht nur verschiebbar, sondern auch Drop-Ziele. Helfen Sie dem Nutzer, dies zu verstehen, indem Sie den Rahmen gestrichelt darstellen lassen, wenn er ein verschobenes Element über eine Spalte hält. In Ihrem CSS können Sie beispielsweise eine over-Klasse für Elemente erstellen, die Drop-Ziele sind:

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

Richten Sie dann in JavaScript die Event-Handler ein und fügen Sie die Klasse over hinzu, wenn die Spalte dorthin gezogen wird, und entfernen Sie sie, wenn das gezogene Element verlässt. Im dragend-Handler entfernen wir außerdem die Klassen am Ende des Ziehens.

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 sind einige Punkte zu beachten:

  • Die Standardaktion für das dragover-Ereignis besteht darin, das Attribut dataTransfer.dropEffect auf "none" festzulegen. Die Eigenschaft dropEffect wird weiter unten auf dieser Seite behandelt. Fürs Erste sei nur gesagt, dass dadurch das Ereignis drop verhindert wird. Wenn Sie dieses Verhalten überschreiben möchten, rufen Sie e.preventDefault() auf. Es ist auch empfehlenswert, false in diesem Handler zurückzugeben.

  • Der Event-Handler dragenter wird zum Umschalten zwischen der Klasse over 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, wodurch die CSS-Klasse wiederholt aktiviert und deaktiviert wird. Dadurch muss der Browser viel unnötige Rendering-Arbeit leisten, was sich auf die Nutzerfreundlichkeit auswirken kann. Wir empfehlen dringend, Neuzeichnungen zu minimieren. Wenn Sie dragover verwenden müssen, sollten Sie den Ereignis-Listener drosseln oder entprellen.

Drop abschließen

Fügen Sie dem drop-Ereignis einen Event-Listener hinzu, um das Löschen zu verarbeiten. Im drop-Handler musst du das Standardverhalten des Browsers für Drop-Vorgänge verhindern, was in der Regel eine Art nervige Weiterleitung ist. Rufen Sie dazu e.stopPropagation() auf.

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

Achten Sie darauf, den neuen Handler zusammen mit den anderen Handlern zu registrieren:

  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 diesem Punkt ausführen, wird das Element nicht an den neuen Speicherort verschoben. Verwenden Sie dazu das Objekt DataTransfer.

Die Property dataTransfer enthält die Daten, die bei einer Drag-Aktion gesendet wurden. dataTransfer wird im dragstart-Ereignis festgelegt und im Drop-Ereignis gelesen oder verarbeitet. Wenn Sie e.dataTransfer.setData(mimeType, dataPayload) aufrufen, können Sie den MIME-Typ und die Datennutzlast des Objekts festlegen.

In diesem Beispiel lassen wir die Nutzenden die Reihenfolge der Spalten neu anordnen. Dazu müssen Sie zuerst den HTML-Code des Quellelements speichern, wenn der Drag-Vorgang 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 das Löschen der Spalte. Dazu setzen Sie den HTML-Code der Quellspalte auf den HTML-Code der Zielspalte, in der die Daten abgelegt wurden. Dazu gehört auch, darauf zu achten, dass der Nutzer nicht in die Spalte zurückkehrt, aus der er gezogen wurde.

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

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

  return false;
}

Das Ergebnis sehen Sie in der folgenden Demo. Dafür benötigen Sie einen Desktop-Browser. Die Drag-and-drop-API wird auf Mobilgeräten nicht unterstützt. Ziehen Sie die Spalte A auf die Spalte B und lassen Sie die Maustaste los. Die Spalten wechseln die Position:

Weitere Eigenschaften für das Ziehen

Das dataTransfer-Objekt stellt Attribute bereit, um dem Nutzer während des Drag-Vorgangs visuelles Feedback zu geben und zu steuern, wie jedes Drop-Ziel auf einen bestimmten Datentyp reagiert.

  • Mit dataTransfer.effectAllowed wird die Art des Ziehens eingeschränkt, die der Nutzer für das Element ausführen kann. Sie wird im Drag-and-drop-Verarbeitungsmodell verwendet, um die dropEffect während der dragenter- und dragover-Ereignisse zu initialisieren. Das Attribut kann die folgenden Werte haben: none, copy, copyLink, copyMove, link, linkMove, move, all und uninitialized.
  • Mit dataTransfer.dropEffect wird das Feedback gesteuert, das der Nutzer während der Ereignisse dragenter und dragover erhält. Wenn der Nutzer den Mauszeiger über ein Zielelement hält, gibt der Cursor des Browsers an, welche Art von Vorgang stattfinden wird, z. B. Kopieren oder Verschieben. Der Effekt kann einen der folgenden Werte haben: none, copy, link oder 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 Drag-Quelle als auch als Drag-Ziel verwendet. Dies kann in einer UI passieren, in der Nutzer aufgefordert werden, Elemente neu anzuordnen. In einigen Situationen können Ziehziel und Quelle unterschiedliche Elementtypen sein, z. B. in einer Oberfläche, in 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 im drop-Handler. Statt dataTransfer.getData() für den Zugriff auf die Dateien zu verwenden, sind die Daten im Attribut dataTransfer.files enthalten:

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 finden Sie unter Benutzerdefiniertes Drag-and-drop.

Weitere Ressourcen