JavaScript/Drag & Drop
Informationen zu diesem Text
- Lesedauer
- 20min
- Schwierigkeitsgrad
- mittel
- Vorausgesetztes Wissen
- JavaScript
Wussten Sie eigentlich schon, dass sich Bilder, Anker und markierter Text auch ohne spezielle Festlegung mit der Maus ziehen lassen? Probieren Sie es mit dem Logo links oben und den Elementen dieser Webseite einmal aus! Es ist allerdings nicht möglich, diese Elemente irgendwo abzulegen (mit Ausnahme eines input-Elements).
Seit die Computer-Maus das Ziehen und Ablegen von Bildern und Icons ermöglichte, erfreut sich diese intuitive und benutzerfreundliche Methode großer Beliebtheit. Dieses Drag & Drop wurde von Microsoft schon 1999 im IE5 eingeführt. In HTML5 sorgt die Drag & Drop-API nun für nativen Browser-Support, ohne dass externe Frameworks notwendig sind.[1]
Details: caniuse.com
Trotz kritischer Stimmen, die die Menge der möglichen Events und die umständliche Handhabung beim Ablegen bemängeln, ist eine einheitliche Spezifikation und nativer Browser-Support doch zu begrüßen.[2] Grobe Ungereimtheiten wie eine CSS-Deklaration -khtml-user-drag: element;
als Ersatz für das damals in Safari fehlende draggable-Attribut sind mittlerweile nicht zuletzt dank der einheitlichen Spezifikation beseitigt.
Inhaltsverzeichnis
HTML[Bearbeiten]
Eigentlich ist Drag und Drop ja eine HTML5-API, die bereits im HTML ansetzt. Dafür gibt es nun einige neue Universalattribute:
draggable[Bearbeiten]
Mit dem neuen Universalattribut draggable="true"
können Sie jedes Element ziehbar machen, bzw. das Ziehen von Bildern und Links durch Setzen von draggable="false"
unterbinden.
Folgende Werte sind erlaubt:
true
: das Element kann gezogen und verschoben werdenfalse
: das Element kann nicht gezogen und verschoben werden
<img id="drag1" src="Selfhtml-logo.gif" alt="Spinnen-Logo" draggable="false" ondragstart="drag(event)">
<img id="drag2" src="2006_patrick_rudolph.gif" draggable="true" ondragstart="drag(event)">
<img id="drag3" src="2006-ani_kirsten-evers.gif" draggable="true" ondragstart="drag(event)">
Im Beispiel sind die beiden Weihnachts-Logos mit draggable=true
ziehbar gemacht worden.
Um zu verdeutlichen, was gezogen werden darf und was nicht, können Sie mit CSS die Darstellung ändern:
[draggable=true] {
cursor: move;
}
[draggable=false] {
cursor: not-allowed;
}
Mit der CSS-Eigenschaft cursor:move
wird der Maus-Zeiger während des Ziehens angepasst. Die Einstellung cursor: not-allowed;
signalisiert bei draggable="false"
das Ziehverbot.
dropzone[Bearbeiten]
Sie können gezogene Objekte in input- und textarea- sowie mit contenteditable ausgezeichnete Elemente ablegen. Ein in der HTML5-Spezifikation befindliches dropzone-Attribut wurde aber noch von keinem Browser implementiert.
Wenn Sie andere Elemente zum Ablegen verwenden wollen, müssen Sie diese mit dem drop-Event verbinden:
<div id="ziel" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData('text', ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
ev.target.appendChild(document.getElementById(data));
}
</script>
JavaScript[Bearbeiten]
Bereits hier ist klar, dass Drag und Drop nur mit dem Einsatz von JavaScript funktioniert.
drag-Events[Bearbeiten]
Die API bietet eine Vielzahl von Events, um das Ziehen und Ablegen möglichst genau steuern zu können.
-
dragstart
: Sobald Sie ein Element mit gedrückter Maus ziehen, wird derdragstart
-Event gefeuert. Er übergibt dem dataTransfer-Objekt die nötigen Informationen. -
drag
: feuert, solange Sie ein Element oder eine Textauswahl ziehen. Das mit event.Target ermittelte Target ist das bewegte Element. -
dragover
: löst aus, wenn Sie ein Element oder eine Textauswahl auf/über ein gewünschtes Ziel gezogen haben. Das mit event.Target ermittelte Target ist das Element, über dem man gerade mit dem bewegten Element schwebt.Beachten Sie: Das ist ein entscheidender Unterschied, denn in dragover kann man prüfen, ob der Cursor gerade über einem drop-fähigen Element schwebt (mangels droppable-Attribut) und entsprechend den dropEffect setzen. -
dragenter
: löst aus, wenn Sie ein Element oder eine Textauswahl zum gewünschten Ziel gezogen haben. -
dragleave
: dragleave feuert, wenn Sie ein Element oder eine Textauswahl wieder vom gewünschten Ziel wegziehen. -
drop
: löst aus, wenn Sie ein Element oder eine Textauswahl abgelegt haben. -
dragend
: wird ausgelöst, wenn der Ziehvorgang vorzeitig abgebrochen wird.
document.addEventListener('dragstart', function(event) {
event.dataTransfer.setData('Text', event.target.id);
document.getElementById('anzeige1').innerHTML = 'dragstart: ausgelöst';
document.getElementById('anzeige1').className = 'start';
});
document.addEventListener('drag', function(event) {
event.dataTransfer.setData('Text', event.target.id);
document.getElementById('anzeige2').innerHTML = 'drag: wird gezogen';
document.getElementById('anzeige2').className = 'aktion';
});
document.addEventListener('dragenter', function(event) {
if ( event.target.className == "droptarget" ) {
document.getElementById('anzeige4').innerHTML = 'dragenter:<br> "Sie haben ihr Ziel erreicht"';
document.getElementById('anzeige4').className = 'ziel';
event.target.style.backgroundColor = '#ebf5d7';
}
});
...
DataTransfer Objekt[Bearbeiten]
Der Vorgang des Ziehens wäre nicht möglich, ohne dass die Ereignisse in einem DataTransfer
-Objekt gespeichert werden können. Deshalb haben alle drag-Events eine dataTransfer
-Eigenschaft, die mit folgenden Methoden gesetzt und ausgelesen werden kann:
dataTransfer.effectAllowed=value
: gibt den Typ der erlaubten Aktion zurück. Mögliche Werte sind:none
: Objekt darf nicht bewegt werdencopy
: kopiert Objekt in die DropzonecopyLink
: copy oder link ist erlaubtcopyMove
: copy oder move ist erlaubtlink
: ein Link kann an der neuen Stelle eingerichtet werdenlinkMove
: link oder move ist erlaubtmove
: darf bewegt werdenall
: alle Funktionen sind erlaubtuninitialized
: alle Funktionen sind erlaubt
dataTransfer.setData(Format, Data)
: setzt Daten und ihr Format. Mögliche Werte wären:text/plain
text/uri-list
image/jpeg
dataTransfer.getData(Format)
: gibt Datenwerte ausdataTransfer.clearData(Format)
: entfernt gepeicherte DatenwertedataTransfer.setDragImage(Element, x, y)
: erzeugt ein ziehbares Bild, die x und y Koordinaten legen den Ort des Mauszeigers fest (0, 0 wäre die linke, obere Ecke).
Anwendungsbeispiel[Bearbeiten]
Die oberen Beispiele sollten nur demonstrieren, wie wenig JavaScript-Code nötig ist. Im Folgenden sehen Sie nun, wie ein solches Beispiel logisch aufgebaut ist:
<div id="div1" ondrop="ablegen(event)" ondragover="ablegenErlauben(event)">
<p draggable="true" ondragstart="ziehen(event)" id="drag1"></p>
<p draggable="true" ondragstart="ziehen(event)" id="drag2"></p>
<p draggable="true" ondragstart="ziehen(event)" id="drag3"></p>
</div>
<div id="div2" ondrop="ablegen(event)" ondragover="ablegenErlauben(event)">
<p draggable="true" ondragstart="ziehen(event)" id="drag4"></p>
<p draggable="true" ondragstart="ziehen(event)" id="drag5"></p>
<p draggable="true" ondragstart="ziehen(event)" id="drag6"></p>
</div>
function ablegenErlauben(ev) {
ev.preventDefault();
}
function ziehen(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function ablegen(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
Die Events sind mit Event-Attributen an die HTML-Elemente gehängt, im nächsten Beispiel zeigen wir Ihnen, wie es eleganter geht.
Grundschritte[Bearbeiten]
Zuerst müssen Sie die Kugeln mittels draggable
ziehbar machen.
<p draggable="true" id="kugel1"> </p>
Im oberen Beispiel wurden die Ereignisse durch Event-Attribute angehängt – hier wird das Ganze dynamisch erledigt:
window.addEventListener("load",function () {
var elms = document.querySelectorAll("[draggable=true]")
for (var i = 0; i < elms.length; i++) {
var draggable = elms[i];
draggable.addEventListener("dragstart",ziehen);
};
});
draggable="true"
ermittelt. In einer Schleife werden dann durch addEventListener die Event-Handler dynamisch angebunden.
function ziehen(ev) {
ev.dataTransfer.setData('text', ev.target.id);
}
ziehen()
setzt die dataTransfer-Eigenschaft. Auch wenn in den Kugeln kein Text vorhanden ist, gelten sie doch als Text.Ablegen[Bearbeiten]
Sobald Sie sich mit dem gezogenen Objekt über der Zielzone befinden, wird das dragover
-Event ausgelöst. Um das Ablegen zu ermöglichen, müssen Sie dieses mit preventDefault "abschalten".
Erst dann darf abgelegt werden:
function ablegenErlauben(ev) {
ev.preventDefault();
}
function ablegen(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData('text');
var target = ev.target;
while (" "+target.className+" ".indexOf(" dropzone ") == -1) target = target.parentNode;
target.appendChild(document.getElementById(data));
}
window.addEventListener("load",function () {
var elms = document.querySelectorAll(".zielzone");
for (var i = 0; i < elms.length; i++) {
var zielzone = elms[i];
zielzone.addEventListener("drop",ablegen);
zielzone.addEventListener("dragover",ablegenErlauben);
};
elms = document.querySelectorAll("[draggable=true]")
for (var i = 0; i < elms.length; i++) {
var draggable = elms[i];
draggable.addEventListener("dragstart",ziehen);
};
});
ablegenErlauben()
wird das dragover-Event mit preventDefault abgeschaltet. Dann kann abgelegt werden. In der Funktion ablegen()
wird der Wert der dataTransfer-Eigenschaft mit getData('text') ausgelesen und die Kugel dann abgelegt.
Ist Ihnen aufgefallen, dass man im oberen Beispiel eine Kugel auch innerhalb einer anderen Kugel ablegen konnte?
Das lag nur daran, dass in der Funktion ablegen()
das target nicht explizit auf das div eingestellt worden war - was sich mit zwei zusätzlichen Zeilen problemlos beheben lässt:
" "+target.className+" ".indexOf(" zielzone ") == -1
ist nichts anderes als ein relativ smarter Test darauf, ob ein Element (target) die Klasse zielzone
enthält; da className ein String mit den leerzeichenseparierten Klassennamen ist, findet diese Methode ein zielzone
sowohl in "zielzone andereKlasse"
als auch in "andereKlasse zielzone dritteKlasse"
als auch in "zielzone"
, da am vorderen String pauschal Leerzeichen angehängt wurden. Das ist leider noch nötig, weil IE9 noch nicht offiziell tot ist und kein element.classList unterstützt - ansonsten wäre target.classList.contains("zielzone")
das Mittel der Wahl.Anwendungsbeispiele[Bearbeiten]
- File Upload - Auswahl mit Drag und Drop
- Zuordnungsquiz (Sortiere in Kategorien)
- Landkarten-Quiz (Ordne Namen zu)
Weblinks[Bearbeiten]
- ↑ W3C: Drag and Drop
- ↑ quirksmode: The HTML5 drag and drop disaster
- ↑ caniuse: Drag and Drop
- WHATWG: 6.9 Drag and drop
- MDN: HTML Drag and Drop API
draggable
auffalse
gesetzt wurde.Sobald ein Drag-Event ausgelöst wird, wird die dazu passende Funktion aufgerufen, die dann die Ereignisse kommentiert.