Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Ziehoperationen

Zentral für die Drag-and-Drop-API sind die verschiedenen Ziehen-Ereignisse, die in einer bestimmten Reihenfolge ausgelöst werden und in einer bestimmten Weise behandelt werden sollen. Dieses Dokument beschreibt die Schritte, die während einer Drag-and-Drop-Operation auftreten, und was die Anwendung innerhalb jedes Handlers tun soll.

Auf hoher Ebene sind hier die möglichen Schritte in einer Drag-and-Drop-Operation:

  • Der Benutzer startet das Ziehen an einem Quellknoten; das dragstart-Ereignis wird am Quellknoten ausgelöst. Innerhalb dieses Ereignisses bereitet der Quellknoten den Kontext für die Ziehoperation vor, einschließlich der Ziehdaten, Feedbackbild und erlaubten Ablageeffekten.
  • Der Benutzer zieht das Element herum: Jedes Mal, wenn ein neues Element betreten wird, wird das dragenter-Ereignis für dieses Element ausgelöst, und das dragleave-Ereignis wird für das vorherige Element ausgelöst. Alle paar hundert Millisekunden wird ein dragover-Ereignis für das Element ausgelöst, in dem sich das Ziehen gerade befindet, und das drag-Ereignis wird am Quellknoten ausgelöst.
  • Das Ziehen erfolgt in ein gültiges Ziel: Das Ziel storniert sein dragover-Ereignis, um anzuzeigen, dass es ein gültiges Ablageziel ist. Eine Form von Ablage-Feedback zeigt dem Benutzer den erwarteten Ablageeffekt an.
  • Der Benutzer führt das Ablegen durch: Das drop-Ereignis wird für das Ablageziel ausgelöst. Innerhalb dieses Ereignisses liest der Zielknoten die Ziehdaten.
  • Die Ziehoperation endet: Das dragend-Ereignis wird am Quellknoten ausgelöst. Dieses Ereignis wird unabhängig davon ausgelöst, ob das Ablegen erfolgreich war oder nicht.

Starten eines Zugs

Das Ziehen beginnt an einem ziehbaren Element, das eine Auswahl, ein ziehbares Element (einschließlich Links, Bilder und jedes Element mit draggable="true"), eine Datei aus dem Dateiexplorer des Betriebssystems usw. sein kann. Zuerst wird das dragstart-Ereignis am Quellknoten ausgelöst, bei dem es sich um das ziehbare Element handelt oder, bei Auswahlen, um den Textknoten, auf dem das Ziehen begann. Wenn dieses Ereignis abgesagt wird, wird die Ziehoperation abgebrochen. Andernfalls wird auch das pointercancel-Ereignis am Quellknoten ausgelöst.

Das dragstart-Ereignis ist die einzige Zeit, in der Sie das dataTransfer ändern können. Für ein benutzerdefiniertes ziehbares Element möchten Sie fast immer die Ziehdaten ändern, was im Detail in Ändern des Ziehdatenspeichers behandelt wird. Es gibt zwei weitere Dinge, die Sie ändern können: das Feedbackbild und die erlaubten Ablageeffekte.

In diesem Beispiel fügen wir einen Listener für das dragstart-Ereignis hinzu, indem wir die addEventListener()-Methode verwenden.

html
<p draggable="true">This text <strong>may</strong> be dragged.</p>
js
const draggableElement = document.querySelector('p[draggable="true"]');
draggableElement.addEventListener("dragstart", (event) => {
  event.dataTransfer.setData("text/plain", "This text may be dragged");
});

Sie könnten auch einem höheren Vorfahren lauschen, da Drag-Ereignisse wie die meisten anderen Ereignisse nach oben blubbern. Aus diesem Grund ist es üblich, auch das Ziel des Ereignisses zu überprüfen, damit das Ziehen einer Auswahl, die innerhalb dieses Elements enthalten ist, nicht das setData auslöst (obwohl das Auswählen von Text innerhalb des Elements schwierig ist, ist es nicht unmöglich):

js
draggableElement.addEventListener("dragstart", (event) => {
  if (event.target === draggableElement) {
    event.dataTransfer.setData("text/plain", "This text may be dragged");
  }
});

Festlegen des Drag-Feedbackbildes

Wenn ein Ziehen auftritt, wird ein durchscheinendes Bild aus dem Quellknoten erzeugt, das dem Zeiger des Benutzers während des Ziehens folgt. Dieses Bild wird automatisch erstellt, sodass Sie es nicht selbst erstellen müssen. Sie können jedoch setDragImage() verwenden, um ein benutzerdefiniertes Drag-Feedbackbild anzugeben.

js
draggableElement.addEventListener("dragstart", (event) => {
  event.dataTransfer.setDragImage(image, xOffset, yOffset);
});

Drei Argumente sind notwendig. Das erste ist ein Verweis auf ein Bild. Dieser Verweis wird in der Regel auf ein <img>-Element verweisen, kann aber auch auf <canvas> oder jedes andere Element verweisen. Das Feedbackbild wird aus dem generiert, wie das Bild auf dem Bildschirm aussieht, obwohl Bilder in ihrer Originalgröße gezeichnet werden. Die zweite und dritte Argumente der setDragImage()-Methode sind Offset-Werte, wo das Bild relativ zum Mauszeiger erscheinen soll.

Sie können auch Bilder und Canvases verwenden, die sich nicht in einem Dokument befinden. Diese Technik ist nützlich, wenn benutzerdefinierte Drag-Bilder mit dem Canvas-Element gezeichnet werden, wie im folgenden Beispiel:

js
draggableElement.addEventListener("dragstart", (event) => {
  const canvas = document.createElement("canvas");
  canvas.width = canvas.height = 50;

  const ctx = canvas.getContext("2d");
  ctx.lineWidth = 4;
  ctx.moveTo(0, 0);
  ctx.lineTo(50, 50);
  ctx.moveTo(0, 50);
  ctx.lineTo(50, 0);
  ctx.stroke();

  event.dataTransfer.setDragImage(canvas, 25, 25);
});

In diesem Beispiel machen wir ein Canvas zum Drag-Bild. Da das Canvas 50×50 Pixel groß ist, verwenden wir Offsets von der Hälfte davon (25), damit das Bild zentriert auf dem Mauszeiger erscheint.

Über Elemente ziehen und Ablageziele angeben

Während des gesamten Verlaufs der Ziehoperation werden alle Eingabegeräteereignisse (wie Maus oder Tastatur) unterdrückt. Die gezogenen Daten können über verschiedene Elemente im Dokument oder sogar über Elemente in anderen Dokumenten bewegt werden. Jedes Mal, wenn ein neues Element betreten wird, wird ein dragenter-Ereignis für dieses Element ausgelöst, und ein dragleave-Ereignis wird für das vorherige Element ausgelöst.

Hinweis: dragleave wird immer nach dragenter ausgelöst, sodass das Ziel konzeptionell zwischen diesen beiden Ereignissen in ein neues Element eingetreten ist, aber das vorherige noch nicht verlassen hat.

Alle paar hundert Millisekunden werden zwei Ereignisse ausgelöst: ein drag-Ereignis am Quellknoten und ein dragover-Ereignis am Element, in dem sich das Ziehen gerade befindet. Die meisten Bereiche einer Webseite oder Anwendung sind keine gültigen Orte zum Ablegen von Daten, sodass Elemente standardmäßig jedes Ablegen ignorieren, das darauf passiert. Das Element kann sich selbst als gültiges Ablageziel wählen, indem es das dragover-Ereignis absagt. Wenn das Element ein bearbeitbares Textfeld ist, wie ein <textarea> oder <input type="text">, und der Datenspeicher ein text/plain-Element enthält, dann ist das Element standardmäßig ein gültiges Ablageziel, ohne dragover abzusagen.

html
<div id="drop-target">You can drag and then drop a draggable item here</div>
js
const dropElement = document.getElementById("drop-target");

dropElement.addEventListener("dragover", (event) => {
  event.preventDefault();
});

Hinweis: Die Spezifikation erfordert, dass auch das dragenter-Ereignis für ein Ablageziel abgesagt wird, andernfalls würden die dragover- oder dragleave-Ereignisse nicht einmal für dieses Element ausgelöst; in der Praxis wird dies von keinem Browser implementiert, und das "aktuelle Element" ändert sich jedes Mal, wenn ein neues Element betreten wird.

Hinweis: Die Spezifikation erfordert, dass das Abbrechen des drag-Ereignisses das Ziehen abbricht; in der Praxis wird dies von keinem Browser implementiert. Siehe folgendes Beispiel:

Bedingte Ablageziele

In der Regel möchten Sie, dass das Ablageziel nur in bestimmten Situationen Ablegen akzeptiert (zum Beispiel nur, wenn ein Link gezogen wird). Um dies zu tun, prüfen Sie eine Bedingung und brechen Sie das Ereignis nur dann ab, wenn die Bedingung erfüllt ist. Zum Beispiel können Sie überprüfen, ob die gezogenen Daten Links enthalten:

js
dropElement.addEventListener("dragover", (event) => {
  const isLink = event.dataTransfer.types.includes("text/uri-list");
  if (isLink) {
    event.preventDefault();
  }
});

In diesem Beispiel verwenden wir die includes-Methode, um zu prüfen, ob der Typ text/uri-list in der Liste der Typen vorhanden ist. Wenn ja, werden wir das Ereignis abbrechen, damit ein Ablegen erlaubt wird. Wenn die Ziehdaten keinen Link enthalten, wird das Ereignis nicht abgebrochen, und ein Ablegen kann an dieser Position nicht stattfinden.

Ablage-Feedback

Jetzt zieht der Benutzer in ein gültiges Ablageziel. Es gibt mehrere Möglichkeiten, den Benutzer darauf hinzuweisen, dass an dieser Position ein Ablegen erlaubt ist und was möglicherweise passiert, wenn das Ablegen stattfindet. Normalerweise wird der Mauszeiger je nach Wert der dropEffect-Eigenschaft entsprechend aktualisiert. Obwohl das genaue Erscheinungsbild von der Plattform des Benutzers abhängt, wird typischerweise ein Pluszeichen-Symbol für ein copy angezeigt, zum Beispiel, und ein "Hier kann nicht abgelegt werden"-Symbol erscheint, wenn ein Ablegen nicht erlaubt ist. Dieses Mauszeiger-Feedback ist in vielen Fällen ausreichend.

Ablageeffekte

Beim Ablegen können mehrere Operationen durchgeführt werden:

copy

Die Daten sind nach dem Ablegen gleichzeitig an Quell- und Ziellocations vorhanden.

move

Die Daten sind nur an der Ziellocation vorhanden und werden von der Quelllocation entfernt.

Eine Form der Verknüpfung wird zwischen Quell- und Abgabelocations erstellt; es gibt nur eine Instanz der Daten an der Quelllocation.

none

Nichts passiert; das Ablegen ist fehlgeschlagen.

Mit den dragenter und dragover-Ereignissen wird die dropEffect-Eigenschaft auf den Effekt initialisiert, den der Benutzer anfordert. Der Benutzer kann den gewünschten Effekt durch Drücken von Modifikatortasten ändern. Obwohl die genauen verwendeten Tasten je nach Plattform variieren, würden normalerweise die Tasten Shift und Control verwendet, um zwischen Kopieren, Verschieben und Verknüpfen zu wechseln. Der Mauszeiger wird sich ändern, um anzugeben, welche Operation gewünscht wird. Bei einem copy könnte der Cursor beispielsweise mit einem Pluszeichen daneben erscheinen.

Sie können die dropEffect-Eigenschaft während der dragenter- oder dragover-Ereignisse ändern, wenn beispielsweise ein bestimmtes Ablageziel nur bestimmte Operationen unterstützt. Sie können die dropEffect-Eigenschaft ändern, um den Benutzereffekt zu überschreiben und eine bestimmte Ablageoperation durchzusetzen.

js
target.addEventListener("dragover", (event) => {
  event.dataTransfer.dropEffect = "move";
});

In diesem Beispiel ist Verschieben der Effekt, der durchgeführt wird.

Sie können den Wert none verwenden, um anzugeben, dass an dieser Position kein Ablegen erlaubt ist. Sie sollten dies in der Regel tun, wenn das Element vorübergehend keine Ablagen akzeptiert; wenn es nicht als Ablageziel vorgesehen ist, sollten Sie einfach das Ereignis nicht absagen.

Beachten Sie, dass das Festlegen von dropEffect nur den gewünschten Effekt zu diesem bestimmten Zeitpunkt anzeigt; ein späteres dragover-Dispatcher kann es ändern. Um die Wahl beizubehalten, müssen Sie sie bei jedem dragover-Ereignis festlegen. Außerdem ist dieser Effekt nur informativ, und welche Effekte letztendlich implementiert werden, hängt sowohl von den Quell- als auch den Zielknoten ab (zum Beispiel, wenn derQuellknoten nicht modifiziert werden kann, dann kann es selbst bei einer "move"-Operation nicht möglich sein, dies durchzuführen).

Für sowohl Benutzerinteraktionen als auch programmatisches Festlegen von dropEffect sind standardmäßig alle drei Ablageeffekte verfügbar. Das ziehbare Element kann sich darauf beschränken, nur bestimmte Effekte zuzulassen, indem es die effectAllowed-Eigenschaft innerhalb eines dragstart-Ereignis-Listeners festlegt.

js
draggableElement.addEventListener("dragstart", (event) => {
  event.dataTransfer.effectAllowed = "copyLink";
});

In diesem Beispiel ist nur eine Kopier- oder Verknüpfungsoperation erlaubt, aber eine Verschiebeoperation kann weder über Skript noch über Benutzerinteraktionen ausgewählt werden.

Die Werte von effectAllowed sind Kombinationen von dropEffect:

Wert Beschreibung
none Keine Operation ist erlaubt
copy Nur copy
move Nur move
link Nur link
copyMove Nur copy oder move
copyLink Nur copy oder link
linkMove Nur link oder move
all copy, move oder link
uninitialized Der Standardwert, wenn der Effekt nicht festgelegt wurde; generell äquivalent zu all, außer der Standardwert für dropEffect ist möglicherweise nicht immer copy.

Standardmäßig wird dropEffect basierend auf effectAllowed in der Reihenfolge von copy, link, move initialisiert und wählt den ersten aus, der erlaubt ist. Die nicht ausgewählten, aber erlaubten Effekte können ebenfalls als Standard ausgewählt werden, wenn dies zutrifft; zum Beispiel führt das Drücken der Alt-Taste unter Windows dazu, dass link vorrangig verwendet wird. Wenn effectAllowed uninitialized ist und das gezogene Element ein <a>-Link ist, ist der Standardwert für dropEffect link; wenn effectAllowed uninitialized ist und das gezogene Element eine Auswahl aus einem editierbaren Textfeld ist, ist der Standardwert für dropEffect move.

Benutzerdefiniertes Ablagefeedback

Für komplexere visuelle Effekte können Sie während des dragenter-Ereignisses andere Operationen ausführen, indem Sie zum Beispiel ein Element an der Position einfügen, an der das Ablegen stattfinden wird. Dies könnte ein Einfügemarker oder ein Element sein, das das gezogene Element an seinem neuen Standort darstellt. Dazu könnten Sie ein <img>-Element erstellen und es während des dragenter-Ereignisses in das Dokument einfügen.

Das dragover-Ereignis wird für das Element ausgelöst, auf das der Mauszeiger zeigt. Natürlich müssen Sie den Einfügemarker innerhalb des dragover-Ereignis-Handlers möglicherweise umherbewegen. Sie können die clientX- und clientY-Eigenschaften des Ereignisses wie bei anderen Mausereignissen verwenden, um den Standort des Mauszeigers zu bestimmen.

Schließlich wird das dragleave-Ereignis ausgelöst, wenn das Ziehen das Element verlässt. Dies ist der Zeitpunkt, zu dem Sie Einfügemarker oder Hervorhebungen entfernen sollten. Sie müssen dieses Ereignis nicht abbrechen. Das dragleave-Ereignis wird immer ausgelöst, selbst wenn das Ziehen abgebrochen wird, sodass Sie immer sicherstellen können, dass eine Bereinigung des Einfügepunkts während dieses Ereignisses durchgeführt werden kann.

Für ein praktisches Beispiel zur Verwendung dieser Ereignisse, siehe unser Kanban-Board-Beispiel.

Ein Ablegen durchführen

Wenn der Benutzer die Maus loslässt, endet die Drag-and-Drop-Operation.

Damit das Ablegen potenziell erfolgreich ist, muss das Ablegen über einem gültigen Ablageziel stattfinden und die dropEffect darf zur Zeit der Mausfreigabe nicht none sein. Andernfalls wird die Ablegeoperation als fehlgeschlagen betrachtet.

Wenn das Ablegen potenziell erfolgreich ist, wird ein drop-Ereignis für das Ablageziel ausgelöst. Sie müssen dieses Ereignis mit preventDefault() abbrechen, damit das Ablegen tatsächlich als erfolgreich betrachtet wird. Andernfalls wird das Ablegen auch dann als erfolgreich angesehen, wenn das Ablegen darin besteht, Text (die Daten enthalten ein text/plain-Element) in ein bearbeitbares Textfeld einzufügen. In diesem Fall wird der Text in das Feld eingefügt (entweder an der Cursorposition oder am Ende, abhängig von den Plattformkonventionen) und, falls die dropEffect move ist, während die Quelle eine Auswahl in einem editierbaren Bereich ist, wird die Quelle entfernt. Andernfalls wird bei allen anderen Drag-Daten und Ablagezielen das Ablegen als fehlgeschlagen betrachtet.

Während des drop-Ereignisses sollten Sie die gewünschten Daten aus dem Ziehdatenspeicher mit DataTransfer.getData() abrufen und an der Ablageposition einfügen. Sie können die dropEffect-Eigenschaft verwenden, um zu bestimmen, welche Ziehoperation gewünscht wurde. Das drop-Ereignis ist die einzige Zeit, in der Sie den Ziehdatenspeicher lesen können, abgesehen von dragstart.

js
target.addEventListener("drop", (event) => {
  event.preventDefault();
  const data = event.dataTransfer.getData("text/plain");
  target.textContent = data;
});

In diesem Beispiel wird, sobald die Daten abgerufen wurden, die Zeichenkette als Textinhalt des Ziels eingefügt. Dies hat den Effekt, den gezogenen Text dort einzufügen, wo er abgelegt wurde, vorausgesetzt, dass das Ablageziel ein Textbereich wie ein p- oder div-Element ist.

Die getData()-Methode gibt eine leere Zeichenkette zurück, wenn der Datenspeicher keine Daten des angegebenen Typs enthält. Wenn Sie bedingte Ablageziele implementiert haben, sollte diese Situation nicht auftreten, da das Ablageziel nur Ablagen akzeptieren sollte, wenn die gewünschten Daten vorhanden sind.

Sie können auch andere Datentypen abrufen. Wenn die Daten ein Link sind, sollten sie den Typ text/uri-list haben. Sie könnten dann einen Link in den Inhalt einfügen.

js
target.addEventListener("drop", (event) => {
  event.preventDefault();
  const lines = event.dataTransfer.getData("text/uri-list").split("\r\n");
  lines
    .filter((line) => !line.startsWith("#"))
    .forEach((line) => {
      const link = document.createElement("a");
      link.href = line;
      link.textContent = line;
      target.appendChild(link);
    });
});

Weitere Informationen zum Lesen von Ziehdaten finden Sie unter Arbeiten mit dem Ziehdatenspeicher.

Es ist außerdem die Verantwortung der Quelle und der Ziel-Elemente, zusammenzuarbeiten, um die dropEffect zu implementieren – die Quelle hört auf das dragend-Ereignis und das Ziel hört auf das drop-Ereignis. Zum Beispiel, wenn die dropEffect move ist, dann muss eines dieser Elemente das gezogene Element von seinem alten Standort entfernen (normalerweise das Quell-Element selbst, da das Ziel-Element nicht unbedingt die Kontrolle über die Quelle hat).

Ein fehlgeschlagenes Ablegen

Die Drag-and-Drop-Operation wird als fehlgeschlagen betrachtet, wenn eine der folgenden Bedingungen zutrifft:

  1. Der Benutzer hat die Escape-Taste gedrückt
  2. Das Ablegen erfolgte außerhalb eines gültigen Ablageziels
  3. Der Ablegeeffekt war zum Zeitpunkt der Mausfreigabe none
  4. Das drop-Ereignis wurde nicht abgebrochen und das Ablegen bestand nicht darin, Text (der text/plain-Daten enthält) in ein bearbeitbares Textfeld einzufügen (siehe ein Ablegen durchführen)

Für die Fälle 1 und 3, wenn der Abbruch während des Schwebens über einem gültigen Ablageziel erfolgt, erhält das Ablageziel ein dragleave-Ereignis, als würde das Ablegen es nicht mehr stattfinden, damit es jedes Ablage-Feedback bereinigen kann. In allen Fällen wird dropEffect für nachfolgende Ereignisse auf none gesetzt.

Anschließend wird ein dragend-Ereignis am Quellknoten ausgelöst. Der Browser kann eine Animation der gezogenen Auswahl anzeigen, die zurück zur Quelle der Drag-and-Drop-Operation geht.

Beenden des Zugs

Sobald das Ziehen abgeschlossen ist, wird ein dragend-Ereignis an der Quelle des Ziehens ausgelöst (dasselbe Element, das das dragstart-Ereignis empfangen hat). Dieses Ereignis wird unabhängig davon ausgelöst, ob das Ziehen erfolgreich war oder nicht.

Hat die dropEffect-Eigenschaft den Wert none während eines dragend, wurde das Ziehen abgebrochen. Andernfalls gibt der Effekt an, welche Operation durchgeführt wurde. Die Quelle kann diese Informationen nach einer move-Operation verwenden, um das gezogene Element von der alten Position zu entfernen.

Ein Ablegen kann im selben Fenster oder über eine andere Anwendung erfolgen. Das dragend-Ereignis wird immer ausgelöst, unabhängig davon, wo es stattfindet. Die screenX- und screenY-Eigenschaften des Ereignisses werden auf die Bildschirmkoordinaten gesetzt, an denen das Ablegen stattgefunden hat.

Nachdem das dragend-Ereignis die Propagation beendet hat, ist die Drag-and-Drop-Operation abgeschlossen.

Siehe auch