draggable
Ziehbar oder draggable ist so ziemlich alles: Bilder, Texte und Element-Blöcke. Das HTML-Attribut draggable="true" alleine reicht aber nicht. Erst die Javascript-Events für Drag & Drop erzeugen den Effekt Ziehen und ablegen.
<img src="image.jpg" draggable="true" …>
Bilder brauchen im Grunde genommen kein draggable="true". Sie lassen sich immer ziehen. Ein p- oder li-Element, ein Button und fast alles andere hingegen läßt sich nur mit dem HTML-Attribut draggable="true" ziehen.
Texte hingegen sind von Haus aus nicht draggable – ziehbar. Wird die Maus mit gedrückter Taste über einen Text gezogen, geht der Browser davon aus, dass der Text kopiert werden soll. Erst das draggable="true" würde ein Ziehen erlauben und gleichzeitig verhindern, dass der Text markiert wird.
<p draggable="true">Texte hingegen sind von Haus aus nicht draggable. … </p>
Der Text lässt sich jetzt zwar ziehen, aber egal ob Text oder Bild – das Element braucht eine Landebahn.
Drag & Drop
Drag Drop braucht ein initiales Ereignis – z.B. wenn die Maus auf ein Element mit dem HTML-Attribut draggable klickt und ohne Loslassen die Maus zieht. Das draggable-Element muss für das drag- oder dragstart-Event registriert sein, die Dropzone für dragover und drop.
- dragstart()
- Event des gezogenen Elements
- drag()
- Event des gezogenen Elements
- dragover()
- Event der Dropzone
- dragenter()
- Event der Dropzone
- dragleave()
- Event der Dropzone
- dragenter()
- Event der Dropzone
- drop()
- Event der Dropzone
Aus der Sicht von Drag & Drop sind die Elemente in zwei Gruppen geteilt: eine Gruppe von ziehbaren Elementen, eine Gruppe von Zielelementen.
Auf den Desktop-Monitor sind Bilder und Links schon ohne draggable-Attribut mit der Maus draggable – sie schlüpfen nur ohne draggable-Script zurück in ihre Position, sobald die Maus losgelassen wird. Safari, Firefox und Chrome zeigen eine transparente Kopie des Elements während des Drags, Microsoft Edge zeigt ein Symbol, das zum Pluszeichen wird, sobald das Dragziel erreicht wird. Um das Mitziehen des Bildes während des Moves muss sich das Skript in diesem einfachen Beispiel also nicht kümmern.
Drag and Drop-Events
Die Webseite braucht ein Element als »Dropzone«. Dropzone ist ein Bereich, auf den das draggable-Element gezogen werden kann.
Die Dropzone ist ein einfaches Element mit etwas CSS, um es innerhalb der Seite herauszustellen.
<div class="dropzone"></div> … <img id="draggedItem" src="hirsch.jpg" alt="Drag and Drop" draggable="true">
Im einfachsten Fall – ein Element auf ein anderes Element ziehen und ablegen – sind nur drei Events gefragt: dragover, dragstart und drop.
const dropzone = document.querySelector (".dropzone");
const draggedItem = document.querySelector ("#draggedItem");
draggedItem.ondragstart = (event) => {
event.dataTransfer.setData ("Text" , event.target.id);
}
event.dataTransfer.setData () transportiert Informationen des gezogenen Elements zur Dropzone. Im Beispiel hier ist das einfach die id des gezogenen Elements. Über diese id greift später das drop-Event der Dropzone auf das Bild zu.
dropzone.ondragover = (event) => { event.preventDefault(); event.currentTarget.style = "background-color:pink"; } dropzone.ondrop = (event) => { event.preventDefault(); const data = event.dataTransfer.getData ("Text"); event.target.appendChild ( document.getElementById (data) ); }
event.preventDefault() unter dem dragover-Event der Dropzone verhindert vorgegebene Aktionen auf dem Element – z.B. navigieren auf eine andere Seite, wenn das Element ein Link ist.
Das vorangegangene Beispiel funktioniert nur auf dem Desktop. Drag & Drop muss für Touchscreens gesondert behandelt werden.
Die Dropzone-Events dragenter und dragleave können über event.currentTarget das Hovern mit der Maus nachahmen, um dem Benutzer besser anzuzeigen, auf welchem Element er das gezogene Element gerade abladen kann.
Drag – Ziehen auf dem Touchscreen
Für den Touchscreen muss das Scrollen während des Ziehens bei touchstart, touchmove und touchend unterbunden werden.
.simpletouch {
position: relative;
max-width: 200px;
height: 100px;
}
.touchdrag {
width: 100px;
height: 100px;
background-color: lightblue;
position: absolute;
touch-action: none; /* Verhindert Standard-Touch-Aktionen wie Scrollen */
}
<div class="simpletouch"> <div id="touchdrag" class="touchdrag"></div> </div>
const draggable = document.getElementById('touchdrag'); let startX, startY, initialX, initialY; draggable.addEventListener('touchstart', function(e) { e.preventDefault(); // Verhindert das native Scrollen const touch = e.touches[0]; startX = touch.clientX; startY = touch.clientY; initialX = draggable.offsetLeft; initialY = draggable.offsetTop; }); draggable.addEventListener('touchmove', function(e) { e.preventDefault(); // Verhindert das native Scrollen const touch = e.touches[0]; const dx = touch.clientX - startX; const dy = touch.clientY - startY; draggable.style.left = initialX + dx + 'px'; draggable.style.top = initialY + dy + 'px'; draggable.style.backgroundColor = "tomato"; }); draggable.addEventListener('touchend', function(e) { draggable.style.backgroundColor = "lightblue" });
Mobile: Drag & Drop auf Touchscreens
Während Drag and Drop mit draggable in den Desktop-Browsern (relativ) problemlos funktioniert: Auf den Touchscreens von iOS kommt es zu Komplikationen. Die Geste »Anfassen mit der Maus und ziehen« ist auf einem Touchscreen schon fürs Scrollen bei »Touch und Ziehen« belegt.
Safari Developer Library: Handling Events
Auf touch-Devices lässt sich Drag und Drop dennoch relativ einfach durch touchmove realisieren.
In diesem Beispiel für Drag & Drop auf dem Touchscreen gibt es zwei mögliche Dropzone-Elemente, die aber auch nur einfache div-Elemente sind.
<div class="boxL"></div> <div class="boxR"></div>
const startbox = document.querySelector('.startbox'); let startx = 0; let starty = 0; const boxL = document.querySelector('.boxL'); const boxR = document.querySelector('.boxR'); const rectL = boxL.getBoundingClientRect(); const rectR = boxR.getBoundingClientRect();
startx und starty merken sich jede Bewegung des Fingers. getBoundingClientRect gibt die Koordinaten der Dragziele zurück und muss beim Scrollen erneut abgerufen werden.
window.onscroll = function () { rectL = boxL.getBoundingClientRect(); rectR = boxR.getBoundingClientRect(); }
Das changedTouches-Objekt enthält Informationen zum Touch-Event. In changedTouches[0] steckt der erste Finger, der die Touchfläche berührt. changedTouches[0].clientX bzw. changedTouches[0].clientY sind die Positionen des Fingers im Viewport.
Auf dem Touchscreen blockiert preventDefault() vorgegebene Aktionen auf dem Element: Normalerweise scrollt die Seite, wenn sie mit einem Finger gezogen wird, ein schnelles doppeltes Tippen würde den Bereich auf dem Touchscreen zoomen.
if (startbox.getAttribute("draggable") && startbox.getAttribute("draggable") === "true") { startbox.addEventListener('touchstart', function(eve){ const touchobj = eve.changedTouches[0]; // erster Finger des touchstart-Events startx = parseInt(touchobj.clientX); // x-Koordinaten des Fingers starty = parseInt(touchobj.clientY); // y-Koordinaten des Fingers eve.preventDefault(); }); startbox.addEventListener('touchmove', function(eve){ const touchobj = eve.changedTouches[0]; // immer noch der erste Finger let distx = parseInt(touchobj.clientX) - startx; let disty = parseInt(touchobj.clientY) - starty; eve.preventDefault(); });
touchmove führt die Position des Fingers laufend nach. touchend feuert, wenn der Finger von der Touchfläche genommen wird. Die Abfrage prüft, ob die letzte Position des Fingers innerhalb einer Dropzone lag: Wenn ja, dann bitte ein Bild fallen lassen.
startbox.addEventListener('touchend', function(eve){ const touchobj = eve.changedTouches[0]; if ( touchobj.clientX > rectL.left && touchobj.clientX < rectL.right && touchobj.clientY > rectL.top && touchobj.clientY < rectL.bottom) { const clone = startbox.cloneNode(startbox); boxL.appendChild(clone); startbox.parentNode.removeChild(startbox); } /** Abfrage nochmal für die rechte Box **/ eve.preventDefault(); }); }
Anders als auf dem Desktop-Monitore, wo ein Element während des Ziehens mit der Maus immer schemenhaft sichtbar bleibt, wird das Bild auf dem Touchscreen beim touchmove nicht angezeigt – da ist Handarbeit angesagt.
Auch wenn das Script für Drag & Drop auf dem Touchscreen länglicher gerät, muss nicht gleich eine umfangreiche Library eingesetzt werden.
dataTransfer.setData / getData
Drag & Drop reagiert nicht nur auf das Ziehen von Elemente innerhalb der Webseite, sondern auch das Laden von Dateien per Drag-Drop vom lokalen Rechner auf den Server ist ein Drag-Drop-Event.
Alle Drag-Events haben eine Eigenschaft dataTransfer. dataTransfer enthält die Daten, die bei einer Drag-und-Drop-Operation verschoben werden.
- Die Methode event.dataTransfer.setData() setzt den Datentyp und die Werte der gezogenen Daten
- Die Methode event.dataTransfer.getData() liest die Daten
dataTransfer.setData hat zwei Parameter: den Typ der Daten und den Wert der Daten, z.B. ("text/plain", "Dieser String zieht um").
Browser-Support
Auf dem Desktop unterstützen alle Browser (IE ab Version 11) Drag und Drop mit draggable. Auf Touchscreens ist Drag und Drop erst mit iOS 11 auf dem iPad / iPhone eingezogen. Für ältere iPads und iPhones sowie für Android muss weiterhin die Lösung mit touchmove herhalten.
Wenn Drag und Drop zu Problemen mit einzelnen Browsern führt: jquery-ndd.js ist eine Library, mit der die Unterschiede in der Implementierung von Drag & Drop in den Browsern eliminiert werden.