CSS, HTML und Javascript mit {stil}

Javascript Drag and Drop

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

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.

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 braucht ein initiales Ereignis – z.B. wenn die Maus auf ein Element mit dem HTML-Attribut draggable klickt. Das draggable-Element muss für das drag- oder dragstart-Event registriert sein, die Dropzone für dragover und drop.

  • dragstart()
  • drag()
  • dragover()
  • dragenter()
  • dragleave()
  • dragenter()
  • drop()

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.

Ziehbares Bild – Javascript draggable
<div class="dropzone"></div>

…

<img id="drag1" src="schnee.png" alt="Drag and Drop" draggable="true">

Auf den Desktop-Monitor sind Bilder und Links schon ohne draggable-Attribut mit der Maus draggable – sie schlüpfen nur ohne draggable sofort 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.

var dropzone = document.querySelector(".dropzone");
var drag1 = document.querySelector("#drag1");

dropzone.ondragover = function (eve) {
   eve.preventDefault();
}

drag1.ondragstart = function (eve) {
   drag(eve);
}

function drag(eve){
   eve.dataTransfer.setData("Text",eve.target.id);
}

dropzone.ondrop = function (eve) {
   eve.preventDefault();
   var data = eve.dataTransfer.getData("Text");
   eve.target.appendChild(document.getElementById(data));
}

eve.preventDefault() 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.

video-tracks
video-tracks
video-tracks
video-tracks

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>
var startbox = document.querySelector('.startbox');

var startx = 0;
var starty = 0;

var boxL = document.querySelector('.boxL');
var boxR = document.querySelector('.boxR');

var rectL = boxL.getBoundingClientRect();
var 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){
      var 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){
      var touchobj = eve.changedTouches[0];  // immer noch der erste Finger
      var distx = parseInt(touchobj.clientX) - startx;
      var 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){
   var touchobj = eve.changedTouches[0]; 
      if ( touchobj.clientX > rectL.left && 
           touchobj.clientX < rectL.right && 
           touchobj.clientY > rectL.top && 
           touchobj.clientY < rectL.bottom) {

         var 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

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.

Drag & Drop