wake-up-neo.com

HTML5 DnD dataTransfer setData oder getData funktioniert nicht in jedem Browser außer Firefox

Betrachten Sie dies JSFiddle . Es funktioniert in Firefox (14.0.1) einwandfrei, schlägt jedoch in Chrome (21.0.1180.75), Safari (?) Und Opera (12.01?) Unter Windows (7) und OS X (10.8) fehl. Soweit ich das beurteilen kann, liegt das Problem entweder bei den setData()- oder getData()-Methoden des dataTransfer-Objekts. Hier ist der relevante Code von JSFiddle.

var dragStartHandler = function (e) {
    e.originalEvent.dataTransfer.effectAllowed = "move";
    e.originalEvent.dataTransfer.setData("text/plain", this.id);
};

var dragEnterHandler = function (e) {
    //  dataTransferValue is a global variable declared higher up.
    //  No, I don't want to hear about why global variables are evil,
    //  that's not my issue.
    dataTransferValue = e.originalEvent.dataTransfer.getData("text/plain");

    console.log(dataTransferValue);
};

Soweit ich das beurteilen kann, sollte dies einwandfrei funktionieren. Wenn Sie beim Ziehen eines Elements auf die Konsole schauen, wird die ID ausgeschrieben. Das bedeutet, dass das Element genau in Ordnung ist und das Attribut id übernommen wird. Die Frage ist, ist es nicht einfach, die Daten einzustellen oder die Daten nicht abzurufen?

Ich würde mich über Vorschläge freuen, denn nach einer Woche Arbeit mit drei Versuchen und mehr als 200 Versionen fange ich an, meinen Verstand zu verlieren. Alles, was ich weiß, ist, dass es in Version 60 oder so verwendet wurde und dass sich der Code überhaupt nicht geändert hat ...

Tatsächlich besteht einer der Hauptunterschiede zwischen 6X und 124 darin, dass ich die Ereignisbindung von live() in on() geändert habe. Ich glaube nicht, dass dies das Problem ist, aber ich habe einige Fehler von Chrome gesehen, wenn es um DnD geht, während ich daran arbeite. Dies wurde entlarvt. Die Ereignisbindungsmethode hat keine Auswirkung auf das Problem.

UPDATE

Ich habe ein neues JSFiddle erstellt, das absolut alles aussortiert und die Ereignisbindung und die Handler verlässt. Ich habe es mit jQuery 1.7.2 und 1.8 sowohl mit on() als auch mit live() getestet. Da das Problem weiterhin bestand, habe ich eine Ebene heruntergesetzt, alle Frameworks entfernt und reines JavaScript verwendet. Das Problem still blieb bestehen, so dass aufgrund meines Tests nicht mein Code fehlschlägt. Stattdessen scheint es, dass Chrome, Safari und Opera alle entweder setData() oder getData() als Spezifikation implementieren oder aus irgendeinem Grund einfach ausfallen. Bitte korrigieren Sie mich, wenn ich falsch liege.

Wenn Sie sich das neue JSFiddle anschauen, sollten Sie in der Lage sein, das Problem zu replizieren. Schauen Sie sich einfach die Konsole an, wenn Sie über ein Element ziehen, das für das Ablegen vorgesehen ist. Ich habe mit Chromium ein Ticket geöffnet. Am Ende mache ich vielleicht immer noch etwas falsch, aber ich weiß einfach nicht, wie ich sonst DnD machen soll. Das neue JSFiddle ist so abgespeckt, wie es nur geht ...

35
Gup3rSuR4c

Ok, also, nachdem ich ein bisschen mehr herumgebuddelt hatte, stellte ich fest, dass das Problem nicht bei Chrome, Safari und Opera liegt. Was es verriet, war, dass Firefox es unterstützt und ich konnte einfach nicht sagen, dass die anderen Browser ausfallen, da ich das normalerweise für den IE akzeptiere.

Die eigentliche Ursache des Problems ist die DnD-Spezifikation selbst . Gemäß der Spezifikation für die Ereignisse drag, dragenter, dragleave, dragover und dragend ist der Drag-Datenspeichermodus geschützter Modus . Was ist geschützter Modus , den Sie fragen? Es ist:

Für alle anderen Veranstaltungen. Die Formate und Arten in der Liste der Drag-Datenspeicherelemente, die gezogene Daten darstellen, können aufgelistet werden. Die Daten selbst sind jedoch nicht verfügbar und es können keine neuen Daten hinzugefügt werden.

Dies bedeutet "Sie haben keinen Zugriff auf die von Ihnen festgelegten Daten, auch nicht im schreibgeschützten Modus! Gehen Sie selbst zu f @ & #." . "Ja wirklich?" Wer ist das Genie, das sich das ausgedacht hat?

Um diese Einschränkung zu umgehen, haben Sie nur wenige Möglichkeiten, und ich werde nur zwei skizzieren, die ich mir ausgedacht habe. Ihre erste ist, eine böse globale Variable zu verwenden und den globalen Namespace zu verschmutzen. Ihre zweite Wahl ist die Verwendung der HTML5 localStorage-API, um genau die Funktionen auszuführen, die die DnD-API zu Beginn hätte bereitstellen sollen!

Wenn Sie diesen Weg gehen, den ich habe, implementieren Sie jetzt zwei HTML5-APIs, nicht weil Sie wollen , sondern weil Sie muss sein. Jetzt beginne ich zu schätzen, dass die HTML5-DnD-API PPKs Schimpfen über die Katastrophe ist.

Im Endeffekt muss die Spezifikation geändert werden, damit auf die gespeicherten Daten zugegriffen werden kann, auch wenn sie sich nur im schreibgeschützten Modus befinden. In meinem Fall, mit diesem JSFiddle , verwende ich das dragenter, um nach vorne in die Drop-Zone zu schauen und zu überprüfen, ob ich einen Drop zulassen soll auftreten oder nicht.

In diesem Fall hat sich Mozilla anscheinend gegen die vollständige Einhaltung der Spezifikation entschieden, weshalb meine JSFiddle darin einwandfrei funktioniert hat. Es ist nur so, dass dies das einzige Mal ist, dass ich die vollständige Spezifikation nicht vollständig unterstütze.

70
Gup3rSuR4c

Es gibt einen Grund für das "geschützte" Bit ... Drag/Drop kann völlig unterschiedliche Fenster umfassen, und sie wollten nicht, dass jemand einen "Listener" -DIV implementieren kann, der den Inhalt von allem belauschen würde darüber gezogen (und diese Inhalte möglicherweise mit AJAX an einen Spionserver in Elbonia senden). Nur der DROP-Bereich (der eindeutig vom Benutzer gesteuert wird) erhält den vollen Umfang.

Ärgerlich, aber ich kann sehen, warum es als notwendig erachtet wird.

15
Jens Fiederer
var dragStartHandler = function (e) {
    e.originalEvent.dataTransfer.effectAllowed = "move";
    e.originalEvent.dataTransfer.setData("text/plain", this.id);
};

Das Problem liegt beim "Text/Plain". Die Standardspezifikation in der MSDN-Dokumentation für setData ist nur "Text" (ohne/plain). Chrome akzeptiert das/plain, aber IE nicht, in jeder Version, die ich versucht habe.

Ich hatte mehrere Wochen lang mit dem gleichen Problem zu kämpfen und versuchte herauszufinden, warum meine "Drop" -Ereignisse in IE nicht richtig ausgelöst wurden, während sie in CHrome waren. Dies liegt daran, dass die Datenübertragungsdaten nicht ordnungsgemäß geladen wurden.

12
Roland

Ich weiß, dass Sie dies bereits beantwortet haben, aber dies ist ein nützlicher Thread - ich wollte hier nur einen Nachtrag hinzufügen. - Wenn Sie die Daten selbst festlegen, können Sie die Daten immer in das Feld selbst einfügen ( Es verhindert jedoch, dass die Funktionalität erneut erstellt werden muss:

Wenn Sie beispielsweise Ihre eigenen benutzerdefinierten Daten festlegen:

  dataTransfer.setData('mycustom/whatever', 'data');

füge die Daten als neuen Dateneintrag hinzu und iteriere:

  dataTransfer.setData('mycustom/whatever/data/{a json encoded string even}');

abfragen:

// naive webkit only look at the datatransfer.types
if (dataTransfer.types.indexOf('mycustom/whatever') >= 0) {

    var dataTest = 'mycustom/whatever/data/';

    // loop through types and create a map
    for (var i in types) {

        if (types[i].substr(0, dataTest.length) == dataTest) {

            // shows:
            // {a json encoded string even}
            console.log('data:', types[i].substr(dataTest.length));

            return; // your custom handler
        }
    }
}

nur in chrom getestet

5
ansiart

Bemerkenswert ist auch, dass wenn Sie die Ausführungskette mit einem Timeout verlassen, das dataTransfer-Objekt Ihre Daten nicht mehr enthält.

function dropEventHandler(event){
    var dt = event.dataTransfer.getData("text/plain"); // works
    var myEvent = event;

    setTimeout(function(){
       var dt = myEvent.dataTranfer.getData("text/plain"); // null
    }, 1);
}
2
Nate Bosscher

Ich bin auf diesen Beitrag gestoßen, weil ich ähnliche Erfahrungen mit den Funktionen dataTransfer.setData() und dataTransfer.getData() von Chrome gemacht habe.

Ich hatte einen Code, der ungefähr so ​​aussah:

HTML:
<div ondragstart="drag(event)" ondrop="newDrop(event)"></div>

JAVASCRIPT:
function drag(ev) {
    ev.dataTransfer.setData("text", ev.target.id);
}
function newDrop(ev){
    var itemDragged = ev.dataTransfer.getData("text");
    var toDropTo = ev.target.id;
}

Der Code hat in Internet Explorer einwandfrei funktioniert, aber als er in Chrome getestet wurde, konnte ich keine Werte in meinem dataTransfer-Objekt (in Drag-Funktion) mithilfe der dataTransfer.getData()-Funktion in der newDrop-Funktion festlegen. Ich konnte den ID-Wert auch nicht aus der Anweisung ev.target.id abrufen.

Nachdem ich einige im Web herumgegraben hatte, stellte ich fest, dass ich die Ereignisparameter currentTarget -Eigenschaft anstelle der events target -Eigenschaft verwenden wollte. Der aktualisierte Code sah ungefähr so ​​aus:

JAVASCRIPT:
function drag(ev) {
    ev.dataTransfer.setData("text", ev.currentTarget.id);
}
function newDrop(ev){
    var itemDragged = ev.dataTransfer.getData("text");
    var toDropTo = ev.currentTarget.id;
}

Mit dieser Änderung konnte ich die Funktionen dataTransfer.setData() und dataTransfer.getData() in Chrome sowie im Internet Explorer verwenden. Ich habe nirgendwo anders getestet und weiß nicht, warum das funktioniert. Hoffe das hilft und hoffentlich kann jemand eine Erklärung geben.

1
Joshua Espana

Ich habe die gleiche Fehlermeldung für den folgenden Code erhalten:

event.originalEvent.dataTransfer.setData ("text/plain", event.target.getAttribute ('id'));

Ich habe den Code geändert in: 

event.originalEvent.dataTransfer.effectAllowed = "verschieben"; event.originalEvent.dataTransfer.setData ("text", event.target.getAttribute ('id'));

Und es hat für mich funktioniert.

1
RoshanZ

Ich habe an einer Website gearbeitet, die mit Firefox getestet wurde.

In WAMP auf meinem Laptop hat Code wie der OP funktioniert. Als ich die Website nach HOSTMONSTER verlegte, funktionierte sie dort jedoch nicht.

Ich folgte Joshua Espanas Antwort und es löste mein Problem.

gescheitert:

ev.dataTransfer.setData("text/plain", ev.target.id);

hat funktioniert:

 ev.dataTransfer.setData("text", ev.currentTarget.id);

Vielen Dank, Joshua!

0
james