Javascript Drag and Drop

Ein einfaches HTML-Attribut draggable erklärt ein Element zum »Drag and Drop«, damit es auf einen anderen Bereich der Webseite gezogen werden kann. Anwendungen für Drag & gibt es reichlich: Warenkörbe, Spiele, Galerien, Tabellen bis zum Umsortieren von Elementen in einem Flex- oder Grid-Raster.

Javascript drag drop – Element ziehen und ablegen für Desktop und Touchscreen

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.

drag-drop kleiner Kaktus
draggable false: kein Ziehen möglich
drag-drop möglich
Das mittlere Bild ist nicht draggable – draggable="false"
<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.

Bild ziehen und ablegen – Javascript drag drop
<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.

drag drop beispiel 1
drag drop example 2
drag drop example 3
drag drop beispiel 4
Häschen? Echt jetzt? Aber Spekulatius gibt es auch immer früher!

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.

Bild ziehbar (draggable) mit Javascript Drag and Drop
<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.

Suchen auf mediaevent.de