In diesem Beitrag werden die Grundlagen von Drag-and-drop erläutert.
Ziehbare Inhalte erstellen
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 AttributdataTransfer.dropEffect
auf"none"
festzulegen. Die EigenschaftdropEffect
wird weiter unten auf dieser Seite behandelt. Fürs Erste sei nur gesagt, dass dadurch das Ereignisdrop
verhindert wird. Wenn Sie dieses Verhalten überschreiben möchten, rufen Siee.preventDefault()
auf. Es ist auch empfehlenswert,false
in diesem Handler zurückzugeben.Der Event-Handler
dragenter
wird zum Umschalten zwischen der Klasseover
anstelle vondragover
verwendet. Wenn Siedragover
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 Siedragover
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 diedropEffect
während derdragenter
- unddragover
-Ereignisse zu initialisieren. Das Attribut kann die folgenden Werte haben:none
,copy
,copyLink
,copyMove
,link
,linkMove
,move
,all
unduninitialized
. - Mit
dataTransfer.dropEffect
wird das Feedback gesteuert, das der Nutzer während der Ereignissedragenter
unddragover
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
odermove
. 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.