HTML5 Drag & Drop, Teil II: Drag & Drop für Elemente

Dieser Artikel ist Teil einer Serie:

  1. Teil 1: Dateien
  2. Teil 2: Elemente
  3. Teil 3: Drop Effect

Die API, die HTML5 für Drag & Drop spezifiziert, gilt gemeinhin als ziemlich missraten. Die Schlimmheit fällt umso stärker aus, je komplexer der Use Case wird, was zur Folge hat, dass das in Teil 1 besprochene Drag & Drop für Dateien noch vergleichsweise simpel war. Auch das Ziehen von Elementen gestaltet sich nicht völlig katastrophal, ist aber ein wichtiger Schritt auf dem Weg in die wirklich üblen Untiefen der API, inklusive erster richtiger Internet-Explorer-Probleme.

Vorbereitungen

Unser Beispiel soll darin bestehen, dass es zwei Dropzonen gibt, in die der Nutzer via Drag & Drop andere Elemente hineinziehen können soll. Dabei soll jedes ziehbare Element nur in eine bestimmte Dropzone hineinpassen. Wie im ersten Teil können uns ganz normale <div>-Elemente als Container dienen, die auch die gleichen Styles wie bisher erhalten:

<div id="Markup" class="dropzone">Markup</div>
<div id="Style" class="dropzone">Style</div>
.dropzone {
  text-align: center;
  background: #EEE;
  border: 0.2em solid #000;
  padding: 3em;
  margin: 0.5em 0;
}

.dropzone.valid {
  background: #EFE;
  border: 0.2em solid #0F0;
}

Als ziehbare Elemente nehmen wir normale <li> mit etwas CSS:

<ul>
  <li>HTML5</li>
  <li>CSS3</li>
  <li>XHTML 1</li>
  <li>JavaScript</li>
</ul>
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
li {
  background: #EEE;
  border: 0.2em solid #000;
  padding: 0.5em;
  margin: 0.5em 0;
}

Außerdem können wir unserem Datei-Drop-Beispiel noch einige Zeilen JavaScript übernehmen. Das Lösen der Drop-Handbremse durch die dragover- und dragenter-Event sowie die Vergabe der Klassen brauchen wir auch diesmal:

$('.dropzone').on('dragover', function(evt){
  evt.preventDefault();
});

$('.dropzone').on('dragenter', function(){
  $(this).addClass('valid');
  evt.preventDefault();
});

$('.dropzone').on('dragleave', function(){
  $(this).removeClass('valid');
});

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  $(this).removeClass('valid');
});

Soweit, so gut (siehe jsFiddle).

Elemente ziehbar machen

Wie in Teil 1 erwähnt, wurde die HTML5-API aus dem IE5 reverse engineered. Dort, sowie auch in allen anderen Browsern, die sie zuvor implementiert hatten, diente Sie zum Abwickeln aller Drag & Drop-Operationen – also auch jener, die ganz ohne JavaScript zustande kommen. Markiert man etwas Text und zieht ihn durch die Gegend oder zieht man ein <a> irgendwo hin, so ist die exakt gleiche Logik wie in der HTML5-API am Werke. Das kann man sehr schön sehen, indem man in der Fiddle einfach irgendwelchen Text (z.B. ein paar Buchstaben aus den Labels der ziehbaren Elemente) markiert und ihn auf eine der Dropzonen zieht; die Zonen leuchten grün auf, weil auch die nativen Drag-Operationen von der API verarbeitet werden können.

Um auch andere Elemente neben <a> ziehbar zu machen, verpasst man ihnen das Attribut draggable="true":

<ul>
  <li draggable="true">HTML5</li>
  <li draggable="true">CSS3</li>
  <li draggable="true">XHTML 1</li>
  <li draggable="true">JavaScript</li>
</ul>

Dieses Attribut wurde im Zuge der HTML5-Entwicklung spontan erfunden, um überhaupt irgendwas anderes als Links und Bilder ziehbar machen. Das draggable-Attribut kann auch genutzt werden, um <a>-Elementen die Drag-Funktionalität entziehen, indem man es auf false setzt. Um das Ganze noch schön zu machen, bietet es sich an, den ziehbaren Elementen auch einen passenden Mauscursor zu verpassen:

[draggable=true] {
  cursor: move;
}

Die Drag-Operation kann durch drei Events via JavaScript verfolgt werden: dragstart feuert beim Start einer Drag-Operation auf dem gezogenen Element, drag findet laufend während einer Operation statt und dragend feuert, wenn eine Drag-Operation endet. Also noch fix ein wenig JS eingebaut …

$('li').on('dragstart', function(evt){
  console.log('Start');
});

$('li').on('drag', function(evt){
  console.log('Drag');
});

$('li').on('dragend', function(evt){
  console.log('Ende');
});

und schon funktioniert das Ziehen von Elementen! Jedenfalls funktioniert es (Stand Anfang 2014) in Chrome; der Firefox weigert sich, Drag & Drop ohne Daten durchzuführen, was (wenn auch durch die Spezifikationen nicht abgedeckt) verständlich ist; im Moment ziehen wir nur Elemente durch die Gegend, ohne dass wir irgendwelche sinnvollen Informationen übermitteln würden. Außerdem können wir aktuell jedes ziehbare Element in jede Dropzone ziehen. Das kann so nicht bleiben – wir brauchen Daten für unsere Drag-Operation.

Daten in Drag & Drop

Eins der Probleme mit Drag & Drop-Daten ist, dass die HTML5-Spezifikationen zwei APIs beschreiben: das gruselige Original aus dem alten IE und eine nicht ganz so gruselige Variante, die bisher nur teilweise in aktuellen Browsern zu finden ist. Das Grundprinzip ist jeweils gleich: beim Start einer Drag-Operation (beim dragstart-Event) schreibt man die zu übertragenden Daten das Event-Objekt. Der Browser merkt sich diese Daten und stellt sie am Ende einer Drag-Operation wieder in einem Event-Objekt zur Verfügung, so dass sich herausfinden lässt, welche Informationen genau durch die Gegend gezogen wurden. Um unser Script in mehr als einem Browser lauffähig zu bekommen, müssen wir uns Anno 2014 noch mit der alten Grusel-API abfinden. Und auf den ersten Blick scheint es auch gar nicht so schlimm zu sein.

Das Datenspeicher-Objekt heißt dataTransfer und ist ein Unterobjekt des Event-Objekts; in den Fiddle-Beispielen ist es dank jQuery unter evt.originalEvent.dataTransfer zu finden. Das Datenspeicher-Objekt hat die Methoden setData(type, data) und getData(type), die zum Setzen und Lesen von Daten genutzt werden können. Das Ganze funktioniert wie ein simpler Key-Value-Speicher für Strings. Auf unser Beispiel aufbauend könnten wir nun beim dragstart-Event Daten setzen und sie beim drop-Event wieder auslesen:

$('li').on('dragstart', function(evt){
  evt.originalEvent.dataTransfer.setData('text', 'Hallo Welt!');
});

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  window.alert(evt.originalEvent.dataTransfer.getData('text'));
});

Und schon funktioniert‘s sogar im Firefox! Allerdings funktioniert es auch nur auf exakt diese Art und Weise, denn der Datenspeicher ist nicht bei jedem Drag & Drop-Event les- und beschreibbar. Laut Spezifikationen bestehen Schreibrechte ausschließlich beim dragstart-Event und Leserechte nur beim drop-Event. Während aller anderen Event-Phasen kann weder gelesen noch geschrieben werden. Das klingt im ersten Moment sinnvoll, hat aber einen Haken: ob ein Ziel-Element ein valides Drop-Ziel ist, bestimmen wir im dragover-Event durch ein preventDefault() – das schon besprochene Lösen der eingebauten Handbremse. Im Moment können wir jedes <li>-Element auf jedes Ziel-<div> ziehen, auch das CSS3-Item in die Markup-Box. Um das zu unterbinden, müssten wir bei dragover in die Daten hineinschauen können, was so richtig nicht geht. Aber immerhin halb! Und nur außerhalb der modernen Internet Explorer. Aber immerhin!

Gezieltes Drag & Drop

In den Event-Phasen neben dragstart und drop kommt man an die Daten im dataTransfer-Objekt nicht heran, wohl aber an die Keys unter denen sie gespeichert sind – und die Spezifikationen sehen vor, dass man den Key frei wählen kann. Also vergeben wir doch einfach je nach Kategorie des gezogenen Elements unterschiedliche Keys. Mit Data-Attributen nehmen wir im Markup die Kategorisierung vor …

<div data-accept="markup" id="Markup" class="dropzone">Markup</div>
<div data-accept="style" id="Style" class="dropzone">Style</div>

<ul>
  <li data-type="markup" draggable="true">HTML5</li>
  <li data-type="style" draggable="true">CSS3</li>
  <li data-type="markup" draggable="true">XHTML 1</li>
  <li data-type="script" draggable="true">JavaScript</li>
</ul>
 … und speichern Daten mit dem Wert des data-type-Attributs der gezogenen Elemente als Key:
$('li').on('dragstart', function(evt){
  console.log('Start');
  var type = $(this).attr('data-type');
  var data = $(this).text();
  evt.originalEvent.dataTransfer.setData(type, data);
});

Die Handbremse zielgerichtet zu lösen scheint auch beinahe einfach zu sein. Im dragover-Event, wo dies passieren müsste, haben wir zwar keinen Zugriff auf den Inhalt von dataTransfer, können aber unter dataTransfer.types die Keys abfragen. Ein Problem hieran ist, dass einige Browser diese Key-Liste (standardkonform) als Array anbieten, andere (ehemals standardkonform) als DOMStringList. Arrays haben eine indexOf()-Methode, die DOMStringLists fehlt, während DOMStringLists eine contains()-Methode haben, die Arrays fehlt. Sobald man diese Differenz irgendwie unter Kontrolle gebracht hat, scheint der restliche Weg ganz leicht zu sein:

  1. Bei dragstart Daten unter einem Key setzen, der dem Wert des data-type-Attributs des gezogenen Elements entspricht
  2. Bei dragover schauen, ob Daten unter einem Key vorhanden sind, der dem Wert des data-accept-Attributs des Ziel-Elements entspricht; wenn ja, mit preventDefault() die Handbremse lösen
  3. Bei drop, das nur stattfindet wenn vorher in dragover ein preventDefault() passiert ist, die eigentlichen Daten auslesen

Den Code für Schritt 1 haben wir breits gesehen. Schritt 2, das dragover-Event, ist auch ganz simpel wenn man erst mal ein gemeinsames indexOf() DOMStringLists und Arrays gebaut hat:

// Universelles indexOf() für DOMStringLists, Arrays und mehr
var indexOf = Function.prototype.call.bind(Array.prototype.indexOf);

$('.dropzone').on('dragover', function(evt){
  var accept = $(this).attr('data-accept');
  // Entspricht ein Key unserem data-accept-Attribut?
  if(indexOf(evt.originalEvent.dataTransfer.types, accept) !== -1){
    evt.preventDefault(); // Drop erlauben!
  }
});

Am Ende ist es ein leichtes, mittels dataTransfer.getData() die übertragenen Daten auszulesen und anzuzeigen:

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  $(this).removeClass('valid');
  var key = $(evt.target).attr('data-accept');
  var val = evt.originalEvent.dataTransfer.getData(key);
  window.alert(val + ' ist ' + key);
});

Und schon klappt es … jedenfalls in Chrome, Opera, Safari und Firefox. In modernen Internet Explorern funktioniert das Script so nicht und eigentlich ist es auch noch nicht so ganz richtig rund.

Lücken, Macken und Internet Explorer

Nur weil Drag & Drop für HTML5 aus dem Internet Explorer reverse engineered wurde, funktioniert es dort noch lange nicht. Grundsätzlich wird die API natürlich schon unterstützt, nur unser Key-Differenzierungs-Trick funktioniert nicht. Die modernen IE akzeptieren ausschließlich text und url als Keys bei dataTransfer.setData() – und das, obwohl der Key-Trick sogar im offiziellen Spezifikations-Tutorial benutzt wird. Das ist etwas unglücklich, aber keine Vollkatastrophe; das einzige, was im IE definitiv nicht geht, ist die Unterscheidung zwischen verschiedenen Sorten von Element-Drag-Operationen. Wenn man nur eine solche Operation auf einer Seite hat (z.B. eine einzige umsortierbare Liste oder eine einzige Dropzone für Dateien) gibt es kein Problem.

Interessanter ist da schon ein noch fehlendes Feature unserer Beispiel-Fiddle. Zwar können wir Elemente von A nach B ziehen und auch sicherstellen, dass A nur in B und nicht in C abgeladen werden kann aber eins können wir nicht: A darüber informieren, dass es nach B gezogen wurde. Möglicherweise möchte man das Element A aus dem DOM verschwinden lassen wenn es irgendwohin gezogen wurde oder in ein anderes Element einsortieren. Das Problem ist, dass wir erst nach dem drop-Event wissen, ob eine Drag-Operation mit einem erfolgreichen Drop geendet ist– das drop-Event feuert allerdings auf dem Ziel-Element B, nicht auf dem gezogenen Element A. Zwar gibt auch das dragend-Event, das wie gewünscht auf A feuert, das allerdings immer tut  auch wenn es gar kein Drop gab. Die gute Nachricht ist: mit ein bisschen Mißbrauch alter IE-Features und der Kunst des indirekten Nachweises ist das Problem in den Griff zu bekommen. Wie genau das geht sehen wir im nächsten Teil dieser Serie.

Hardware-Review: Sony eBook-Reader PRS-T3

Ich bin kein Early Adopter. Weder hatte ich bisher einen E-Reader in meinem Besitz noch habe ich im Nachgang der Snowden-Veröffentlichungen groß etwas gegen meine Ausspionierbarkeit getan. Gerade letzteres betrachte ich als ein langfristiges Projekt; in einer Hau-Ruck-Aktion bei allem und jedem Online-Dienst den Anbieter wechseln bzw. zu einer selbstgehosteten Lösung umschwenken stelle ich mir sehr mühsam und mit Reibungsverlusten behaftet vor. Lieber mache ich sowas allmählich, dafür dann aber gründlich und nervenschonend. Der erste Schritt dabei muss auch gar nicht der Umbau vorhandener Infrastruktur sein, denn es reicht vielleicht erst mal, keine neue Stasi-Hard- und Software anzuschaffen. Und so ging es mir bei der E-Reader-Auswahl dann auch klar darum, ein möglichst simples Gerät zu wählen; eins das im Idealfall gar nicht nach Hause telefonieren oder sonstigen Fernsteuer-Schindluder treiben kann. Ein solches Teil zu finden, das am Ende dann auch noch als Reader taugen soll, war gar nicht einfach.

Wenn man sich durch die einschlägigen Review-Seiten gräbt, gibt es keinen Zweifel: die besten E-Reader fabriziert im Moment Amazon mit dem Kindle, im übrigen auch zum besten Preis. Da der Kindle aber auch im Stasi-Ranking weit vorn liegt, Amazon Ökosystem-Lock-In betreibt und auch darüber hinaus allerlei unschönes Zeug macht, suchte ich eine Alternative. Die diversen Probleme haben in Teilen auch einige Konkurrenzprodukte (wie z.B. erzwungene Online-Aktivierung), aber meist in nicht ganz so schlimm und nicht im gleichen Umfang. Meine Wahl fiel schließlich auf den PRS-T3 Reader von Sony (Sony-Produktseite, Amazon-Link), über den ich nach eingehenden Tests nichts schlechtes zu berichten weiß.

Der Reader macht die Basics alle richtig. Der Text ist gestochen scharf, ein sich leerender Akku ist nicht wahrnehmbar, es passt viel in den Speicher und das Gerät ist klein und leicht. Eine Abdeckung für den Bildschirm ist fest angebaut. Die Bedienung erfolgt primär via Touchscreen, wobei es zum Blättern noch ergänzende Hardware-Tasten gibt. An behandschuten Wintertagen sind diese wirklich sehr sehr nützlich. Der Touchscreen ist ok; smartphoneske Leistungen ergeben sich vermutlich allein schon aus der beschränkten Rechenpower des Geräts nicht. Zum Umblättern und zum Navigieren des UIs reicht es aber locker. Neben der Lese-Funktion gibt es natürlich auch einen Webbrowser und allerlei übrige Apps, die man samt und sonders in der Pfeife rauchen kann, weil das Gerät eben nur zum lesen gebaut ist. Reviews bemängeln die fehlende Hintergrundbeleuchtung, die heutzutage wohl Standard bei E-Readern ist. Mir hat sie bisher nicht gefehlt, aber ich habe auch keine Vergleichserfahrungen (außer mit Totholz-Büchern, ebenfalls ohne eingebaute Beleuchtung).

Zum Thema „Stasifrei und Spaß dabei“: das Gerät kann durchgehend mit abgeschaltetem Wlan betrieben werden, nach Hause telefoniert wird nicht. Neue E-Books lassen sich mit der freien Software Calibre bequem auf den per USB angeschlossenen Reader schaffen und verwalten. Als Bezugsquelle kann man jeden E-Book-Shop im großen weiten WWW nutzen. Der Reader spricht nativ EPUB, PDF und TXT, ergänzend kann Calibre verschiedenste Formate ineinander konvertieren (z.B. MOBI zu EPUB) und im Vorbeigehen auch DRM entfernen. Man ist also ungebunden und – soweit ich das beurteilen kann – unbeobachtet.

So richtig viel zu meckern habe ich über das Gerät also nicht, auch nicht nach drei drauf gelesenen dicken Wälzern. Auf Twitter habe ich allgemeines Entsetzen über die Barabarei wahrgenommen, die ein nicht-hintergrundbeleuchteter Reader darstellt, aber wie schon erwähnt: so richtig hat mir das bisher nicht gefehlt. Wer also jenseits vom Amazoniversum gepflegt und unbeobachtet E-Books lesen möchte, macht mit dem Reader von Sony meiner Meinung nach nicht viel verkehrt.

HTML5 Drag & Drop, Teil I: Drag & Drop für Dateien

Dieser Artikel ist Teil einer Serie:

  1. Teil 1: Dateien
  2. Teil 2: Elemente
  3. Teil 3: Drop Effect

Die Drag & Drop-API von HTML5 entstand, wie das bei Webstandards so üblich ist, durch Reverse Engineering aus einer bereits in einem Browser vorhandenen Technologie. Das wäre auch nicht weiter tragisch, wäre nur die Basis-Technologie in diesem Falle nicht dem Internet Explorer 5 (!) entsprungen. Und ja, sie ist genau so übel, wie man sich das vorstellt; die von PPK gewählt Bezeichnung steaming pile of bovine manure trifft es eigentlich ganz gut.

Nur es hilft alles Jammern nichts, denn es gibt keine Alternative. Zwar kann man mit diversen JavaScript-Bibliotheken schon seit jeher Drag-&-Drop-Funktionen in den Browser bringen, doch diese Lösungen erreichen nicht den Funktionsumfang von HTML5. Vor allem hat sich seit den Betrachtungen von PPK 2009 einiges in Browsern und Entwickler-Gehirnen getan, so dass mittlerweile tatsächlich sinnvolle Dinge mit dieser API möglich sind. Ohne native Browserunterstützung der HTML5-API ist es zum Beispiel nicht möglich, Dateien aus dem Dateisystem in die Webseite zu ziehen und zu verarbeiten. Trotz mißratener API ist das auch gar nicht mal so schwer.

Vorbereitungen

Wenn man eine Datei ins Browserfenster zieht, versteht der Browser das normalerweise als Navigationsanweisung, d.h. er leitet auf die lokale Adresse der gezogenen Datei weiter. Möchte man eine Datei in der Webapp verarbeiten, so muss man mit ein bisschen JavaScript, HTML und CSS nachhelfen. Man nehme etwas Markup für ein Drop-Zielelement …

<div class="dropzone">Drop!</div>

… garniere es mit CSS …

.dropzone {
  text-align: center;
  background: #EEE;
  border: 0.2em solid #000;
  padding: 3em;
}

… und fange die durch HTML5 spezifizierten Drag-&-Drop-Events ab. Hiervon gibt es eine ganze Menge, wobei für den Datei-Drop-Fall nur dragover und drop wirklich relevant sind:

$('.dropzone').on('dragover', function(evt){
});

$('.dropzone').on('drop', function(evt){
});

Soweit, so gut (siehe jsFiddle).

Das Lösen der Handbremse

Warum braucht man nun zwei Events für eine einfache Drop-Operation? Die API von Drag & Drop ist so gestaltet, dass in der Ausgangslage drop-Events nicht stattfinden, wenn man eine Datei auf einem Element droppt. Die Handbremse ist also standardmäßig angezogen. Die Überlegung dahinter war wohl, dass die meisten Elemente in einer Webseite eben keine validen Dropziele darstellen . Für diese Elemente soll weiterhin das normale Verhalten (Drop = Redirect) gelten und nur wenn man die Handbremse löst, soll etwas anderes passieren können. Lösen lässt sich die Bremse, indem man ganz einfach das dragover-Event abbricht:

$('.dropzone').on('dragover', function(evt){
  evt.preventDefault();
});

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  window.alert('Drop!');
});

Ohne das evt.preventDefault() im dragover-Event würde das drop-Event nicht feuern und das Alert würde nicht aufpoppen (Ausnahme: Chrome ist der einzige Browser bei dem dieses Lösen der Handbremse seit einiger Zeit nicht mehr nötig ist). Ein weiteres evt.preventDefault() im drop-Event selbst verhindert schließlich den standardmäßig stattfindenden Redirect. Und damit das Ganze auch im IE funktioniert, braucht es auch ein evt.preventDefault() im dragenter-Event (MSDN). Somit haben wir eine brauchbare Ausgangslage geschaffen, denn wir können zumindest mal Dateien in den Browser ziehen, ohne dass die Standardmechaniken greifen (siehe jsFiddle).

Noch zwei Anmerkungen: die HTML5-Spezifikationen schreiben, dass das dragenter-Event für das Lösen der Handbremse zuständig zu sein habe; in allen Browsern außer dem IE erfüllt tatsächlich nur das dragover-Event diese Funktion. Im IE müssen beide Events abgebrochen werden. Das ebenfalls in den Spezifikationen zu findende dropzone-Attribut ist stand heute ein reiner Papiertiger.

Gezogene Dateien einlesen

Der Rest ist eigentlich relativ einfach. Im Event-Objekt des Drop-Handler gibt es eine Eigenschaft dataTransfer, die wiederum eine Eigenschaft files hat, die eine Liste der gedroppten Dateien darstellt. Dabei handelt es sich um eine ganz normale FileList mit File-Objekten wie in der File API spezifiziert – die Weiterverarbeitung der gezogenen Datei ist also mit den ganz normalen HTML5-Mitteln möglich. Um beispielweise den Inhalt einer Textdatei mit einem Alert-Fenster auszugeben pickt man sich aus der FileList die Datei heraus und liest sie ein:

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  var file = evt.originalEvent.dataTransfer.files[0];
  if(file.type === 'text/plain'){
    var reader = new FileReader();
    reader.readAsText(file);
    reader.addEventListener('load', function(){
      window.alert(reader.result);
    }, false);
  }
});

Dieser Code funktioniert, wenn nur eine Datei gezogen wird oder bei mehreren Dateien die erste Datei eine Plaintext-Datei ist. Das Prinzip auf mehrere Dateien auszudehnen ist ohne weiteres möglich, aber wegen der asynchronen Natur der FileReader-Objekte auch kein kompletter Selbstläufer – wir ersparen uns das an dieser Stelle. Alternativ wäre es natürlich auch möglich, die Dateien in Indexed DB zu speichern, mit XHR2 auf einen Server zu laden oder anderweitig zu verarbeiten.

Erwähnenswert ist, dass der Inhalt des dataTransfer-Objekts wirklich nur beim drop-Event auslesbar ist. Bei allen anderen Events befindet sich das Daten-Objekt im protected mode und die API behauptet, es würden gar keine Dateien gezogen. Das ist zur Zeit ein ziemliches Problem, denn eigentlich möchte man doch das Drop-Ziel hervorheben, wenn der Nutzer eine gültige Datei auf das Element zieht. Aktuell ist dies in keinem Browser möglich, lediglich Drop-Ziele an sich können hervorgehoben werden.

Drop-Ziele hervorheben

CSS Selectors Level 4 spezifiziert Pseudoklassen für Drag & Drop, ist jedoch noch weit davon entfernt in irgendwelchen Browsern aufzuschlagen. Zur Zeit führt der einzig gangbare Weg über JavaScript, speziell die Events dragenter und dragleave. Gestaltet man sich eine hübsche Klasse für gültige Dropziele …

.valid {
  background: #EFE;
  border: 0.2em solid #0F0;
}

… so kann man diese bequem in diesen beiden Events vergeben und wieder entfernen. Außerdem muss die Klasse auch nach einem erfolgten drop entfernt werden, da für Browser offenbar ein Ende der Drop-Operation kein Ende der Drag-Operation darstellt:

$('.dropzone').on('dragenter', function(){
  $(this).addClass('valid');
  evt.preventDefault();
});

$('.dropzone').on('dragleave', function(){
  $(this).removeClass('valid');
});

$('.dropzone').on('drop', function(evt){
  evt.preventDefault();
  // Rest der Drop-Logik
  $(this).removeClass('valid');
});

Fertig! War doch gar nicht mal so schwer.

Fazit

Mit der HTML5-API für Drag & Drop Dateien im Browser zu empfangen ist ohne weiteres möglich. Zwar gibt es ein paar Mängel (z.B. die fehlende Möglichkeit, Dropziele nur bei bestimmten Dateitypen hervorzuheben), aber für einfache wie Use Cases wie Reinziehen-Hochladen-Fertig reicht es allemal. Auch mit der Browserunterstützung sieht es gut aus; der limitierende Faktor dürfte eher sein, ob die HTML5-APIs zur Weiterverarbeitung der Dateien unterstützt werden.

Betrachtet man nur den Datei-Drop, sieht die HTML5-API gar nicht so schlimm aus. Das ändert sich, sobald man versucht auch DOM-Knoten ziehbar zu machen und in Dropzielen zu empfangen. In 90% der Fälle ist man hier mit einer Non-HTML5-Lösung (also z.B. einem beliebigen jQuery-Plugin) besser beraten, falls man nicht etwas sehr bestimmtes vorhat – was genau das ist, sehen wir dann im nächsten Teil.

Erklärbär-Termine für Februar und März 2014

Auch im neuen Jahr startet Erklärbär Tours wieder einige Expeditionen in das Reich von HTML5, CSS3 und JavaScript. Falls ihr dabei sein wollt, habe ich die folgenden Termine für euch im Angebot:

  • 3. - 5. Februar in München: HTML5-Schulung bei der Open Source School. Mein bewährtes drei­tä­gi­ges HTML5-Standardprogramm stattet die Teilnehmer im Druckbetankungsverfahren mit so gut wie allem aus, was man zu HTML5 wissen muss. Von semantischem Markup bis hin zu Canvas-Frameworks ist alles dabei. Geboten wird ein großer Praxisanteil, kleine Arbeitsgruppen und ein Buch gibt es obendrein.
  • 6. und 7. Februar in München: CSS3 bei der Open Source School. Mein zweitä­gi­ges CSS3-Standardprogramm katapultiert die Teilnehmer in das CSS3-Zeitalter, in dem Webfonts und Farbverläufe fließen. Auch hier steht einen großer Praxisanteil mit überschaubaren Arbeitsgruppen auf dem Plan und mindestens ein CSS3-E-Book gibt es als Bonus.
  • 5. - 7. März in Unterhaching: halbtägige Workshops ECMAScript 5 und Web Workers im Rahmen der JavaScript Days 2014.

Folgt mir!

Kauft mein Zeug!

Cover der HTML5-DVD
Infos auf html5-dvd.de, kaufen bei Amazon

Cover des HTML5-Buchs
Infos auf html5-buch.de, kaufen bei Amazon

Cover des CSS3-Buchs
Infos auf css3-buch.de, kaufen bei Amazon