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, Listen umsortieren, Galerien organisieren, Tabellen arrangieren bis zum Umsortieren von Bausteinen Editoren wie Website-Buildern.
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". Auf dem Desktop-Monitor lassen sich mit der Maus 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 musste für Touchscreens gesondert behandelt werden, bis die modernen Browser Pointer Events zuverlässig unterstützten.
Drag auf dem Handy / Touch-Screen
Ohne touch-action: none; würde der Browser auf Touch-Geräten versuchen, zu scrollen oder zu zoomen. touch-action: none; übergibt die Kontrolle an ein Script.
<style> #box { width: 100px; height: 100px; background: tomato; position: absolute; top: 50px; left: 50px; touch-action: none; /* wichtig für Touch-Geräte */ } #boxwrappper { position: relative; height:200px; } </style> <div id="boxwrappper"> <div id="box"></div> </div>
Das eingebaute HTML5 Drag & Drop API (dragstart, drop, etc.) ist stammt noch aus den Anfangsgründen von HTML5 (~2008) und unterstützt Touch-Geräte kaum und wird auch nicht mehr weiterentwickelt. Pointer Events (pointerdown, pointerup, pointermove) übernehmen die Aufgaben des Drag & Drop-Api ohne wirken sowohl mit der Maus, dem Finger als auch mit dem Stift.
const box = document.getElementById('box'); let offsetX = 0, offsetY = 0, dragging = false; box.addEventListener('pointerdown', (e) => { dragging = true; // Position relativ zum Klick merken offsetX = e.clientX - box.offsetLeft; offsetY = e.clientY - box.offsetTop; box.setPointerCapture(e.pointerId); // nur dieser Finger/Maus zieht }); box.addEventListener('pointermove', (e) => { if (!dragging) return; box.style.left = (e.clientX - offsetX) + 'px'; box.style.top = (e.clientY - offsetY) + 'px'; }); box.addEventListener('pointerup', () => dragging = false); box.addEventListener('pointercancel', () => dragging = false);
Pointer Events: Drag Drop
Das alte Drag & Drop-API würde also immer eine Doppelprogrammierung brauchen, um sowohl Touchscreens als auch die Maus auf Desktop-Monitoren zu behandeln. Auch für ein Drag & Drop sind Pointer Events die bessere Lösung.
<div id="dragIt"></div> <div class="greenzone">Drop here</div>
pointerevents-drag-drop herunterladen (zip, 1KB)
body { font-family: sans-serif; height: 100vh; display: flex; align-items: center; justify-content: space-around; } #dragIt { width: 120px; height: 120px; background: url(img/deerblue.webp); background-size: 100%; border-radius: 8px; position: absolute; z-index:1000; top: 10px; left: 10px; cursor: grab; touch-action: none; /* wichtig für Touch */ transition: left 0.3s ease, top 0.3s ease; /* für weiches Zurückspringen */ } .greenzone { width: 180px; height: 180px; border: 3px dashed #999; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: hsl(90,60%,80%); transition: border-color .3s, background .3s; position: relative; margin-left: auto; } .greenzone.active { border-color: limegreen; background: #eaffea; }
const dragIt = document.getElementById('dragIt'); const greenzone = document.querySelector('.greenzone'); let offsetX = 0, offsetY = 0, dragging = false; // Startposition merken let startX = dragIt.offsetLeft; let startY = dragIt.offsetTop; dragIt.addEventListener('pointerdown', (e) => { dragging = true; offsetX = e.clientX - dragIt.offsetLeft; offsetY = e.clientY - dragIt.offsetTop; dragIt.setPointerCapture(e.pointerId); dragIt.style.cursor = "grabbing"; dragIt.style.transition = "none"; // keine Animation beim Ziehen }); dragIt.addEventListener('pointermove', (e) => { if (!dragging) return; const x = e.clientX - offsetX; const y = e.clientY - offsetY; dragIt.style.left = x + 'px'; dragIt.style.top = y + 'px'; // Checke ob über greenzone const rect = greenzone.getBoundingClientRect(); if (e.clientX > rect.left && e.clientX < rect.right && e.clientY > rect.top && e.clientY < rect.bottom) { greenzone.classList.add('active'); } else { greenzone.classList.remove('active'); } }); dragIt.addEventListener('pointerup', (e) => { if (!dragging) return; dragging = false; dragIt.style.cursor = "grab"; const rect = greenzone.getBoundingClientRect(); dragIt.style.transition = "left 0.3s ease, top 0.3s ease"; if (e.clientX > rect.left && e.clientX < rect.right && e.clientY > rect.top && e.clientY < rect.bottom) { greenzone.classList.remove('active'); // dragIt in die Mitte der greenzone schnappen lassen const centerX = rect.left + rect.width/2 - dragIt.offsetWidth/2; const centerY = rect.top + rect.height/2 - dragIt.offsetHeight/2; dragIt.style.left = centerX + "px"; dragIt.style.top = centerY + "px"; } else { greenzone.classList.remove('active'); // dragIt zurück zur Startposition dragIt.style.left = startX + "px"; dragIt.style.top = startY + "px"; } }); dragIt.addEventListener('pointercancel', () => { dragging = false; box.style.cursor = "grab"; greenzone.classList.remove('active'); });
Drag Drop – Liste sortieren
Zu den beliebtesten Anwendungen von Drag & Drop zählt das Sortieren von Listen – wie z.B. im Gutenberg-Editor von WordPress. Die Zutaten kommen auch hier heute aus den Pointer-Events: Liste mit pointerdown, pointermove, pointerup sortieren. Kurz und knapp:
<ul id="sortable"> <li>🍎 Apfel</li> <li>🍌 Banane</li> <li>🍇 Trauben</li> <li>🍊 Orange</li> <li>🍒 Kirsche</li> </ul>
- 🍎 Apfel
- 🍌 Banane
- 🍇 Trauben
- 🍊 Orange
- 🍒 Kirsche
const list = document.getElementById("sortable"); let draggedItem = null; list.addEventListener("pointerdown", (e) => { if (e.target.tagName === "LI") { draggedItem = e.target; draggedItem.setPointerCapture(e.pointerId); draggedItem.classList.add("dragging"); } }); list.addEventListener("pointermove", (e) => { if (!draggedItem) return; if (draggedItem) { e.preventDefault(); } const items = [...list.querySelectorAll("li:not(.dragging)")]; const y = e.clientY; let next = null; for (const item of items) { const box = item.getBoundingClientRect(); if (y < box.top + box.height / 2) { next = item; break; } } list.insertBefore(draggedItem, next); }); list.addEventListener("pointerup", (e) => { if (draggedItem) { draggedItem.classList.remove("dragging"); draggedItem.releasePointerCapture(e.pointerId); draggedItem = null; } });
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").