web-dev-qa-db-fra.com

HTML5 DnD dataTransfer setData ou getData ne fonctionne pas dans tous les navigateurs sauf Firefox

Considérez ceci JSFiddle . Cela fonctionne bien dans Firefox (14.0.1), mais échoue dans Chrome (21.0.1180.75), Safari (?) Et Opera (12.01?) Sous Windows (7) et OS X (10.8). Autant que je sache, le problème concerne les méthodes setData() ou getData() sur l'objet dataTransfer. Voici le code correspondant du 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);
};

Autant que je sache, cela devrait fonctionner parfaitement. Si vous regardez la console en faisant glisser un élément, vous verrez son identifiant écrit, ce qui signifie qu'il trouve l'élément correctement et récupère son attribut id. La question est de savoir s'il ne s'agit pas simplement de définir les données ou de ne pas les obtenir.

J'apprécierais les suggestions car après une semaine de travail avec trois tentatives et plus de 200 versions, je commence à perdre la tête. Tout ce que je sais, c'est que cela fonctionnait dans la version 60 ou plus et que le code spécifique n'a pas changé du tout ...

En fait, l’une des principales différences entre 6X et 124 est que j’ai modifié la liaison d’événement de live() à on(). Je ne pense pas que ce soit le problème, mais je suis arrivé à constater quelques échecs de Chrome en ce qui concerne le traitement de fichiers DnD. Cela a été démystifié. La méthode de liaison d'événement n'a aucun effet sur le problème.

METTRE À JOUR

J'ai créé un nouveau JSFiddle qui supprime absolument tout et laisse simplement la liaison et les gestionnaires d'événement. Je l'ai testé avec jQuery 1.7.2 et 1.8 avec on() et live(). Le problème persistant, j'ai donc abandonné un niveau, supprimé tous les frameworks et utilisé du JavaScript pur. Le problème encore persistait. Par conséquent, d'après mes tests, ce n'est pas mon code qui échoue. Au lieu de cela, il semble que Chrome, Safari et Opera implémentent tous setData() ou getData() off spec ou échouent pour une raison ou une autre. Corrigez-moi si j'ai tort, s'il-vous plait.

Quoi qu'il en soit, si vous jetez un coup d'œil au nouveau JSFiddle, vous devriez pouvoir reproduire le problème. Il suffit de regarder la console lorsque vous faites glisser un élément désigné pour accepter un déplacement. Je suis allé de l'avant et a ouvert un ticket avec Chromium . En fin de compte, je continue peut-être à faire quelque chose de mal, mais je ne sais tout simplement pas comment faire DnD à ce stade. Le nouveau JSFiddle est aussi dépouillé que possible ...

35
Gup3rSuR4c

Ok, donc après un peu plus de recherches, j'ai constaté que le problème ne concernait pas Chrome, Safari et Opera. Ce qui l’a fait comprendre, c’est que Firefox l’a pris en charge et je ne peux tout simplement pas dire que les autres navigateurs échouent, car j’accepterais normalement cela pour IE.

La cause réelle du problème est la spécification DnD elle-même . Selon les spécifications des événements drag, dragenter, dragleave, dragover et dragend, le mode magasin de données déplacé est mode protégé. Qu'est-ce que vous demandez mode protégé? Il est:

Pour tous les autres événements. Formats et types dans le magasin de données de glisser La liste des éléments représentant les données glissées peut être énumérée, mais le les données elles-mêmes ne sont pas disponibles et aucune nouvelle donnée ne peut être ajoutée.

Cela se traduit par "vous n’avez pas accès aux données que vous définissez, pas même en mode lecture seule! Allez f @ vous-même.". Vraiment? Qui est le génie qui est venu avec ça?

Maintenant, pour contourner cette limitation, vous avez peu de choix, et je vais seulement en souligner deux que j'ai proposées. La première consiste à utiliser une variable globale diabolique et à polluer l’espace de nom global. Votre deuxième choix consiste à utiliser l'API HTML5 localStorage pour exécuter la même fonctionnalité EXACT que celle que l'API DnD aurait dû fournir!

Si vous suivez cette voie que j'ai, vous implémentez maintenant deux API HTML5, non pas parce que vous voulez, mais parce que vous avez. Maintenant, je commence à comprendre le discours de PPK sur le désastre l’API HTML5 DnD.

La ligne de fond est la suivante: la spécification doit être modifiée pour permettre l'accès aux données stockées même si elles ne sont qu'en lecture seule. Dans mon cas, avec ce JSFiddle , j'utilise en fait la dragenter comme moyen de regarder en avant la zone de largage et de vérifier que je devrais ou non permettre un largage.

Dans ce cas, Mozilla a apparemment choisi de ne pas se conformer entièrement à la spécification, raison pour laquelle mon JSFiddle fonctionnait parfaitement. Il se trouve que c’est la seule fois où je m’engage à ne pas prendre en charge la spécification complète.

65
Gup3rSuR4c

Il y a une raison pour que le bit "protégé" ... le glisser/déposer puisse s'étendre sur des fenêtres complètement différentes, et ils ne voulaient pas que quelqu'un puisse implémenter une DIV "auditeur" permettant d'écouter le contenu de tout glissé dessus (et peut-être envoyer ce contenu par AJAX à un serveur espion dans Elbonia). Seule la zone DROP (qui est plus clairement sous le contrôle de l'utilisateur) obtient le scoop complet.

Ennuyeux, mais je peux voir pourquoi cela pourrait être considéré comme nécessaire.

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

Le problème est avec le "text/plain". La spécification standard dans la documentation MSDN pour setData est juste "text" (sans le/plain). Chrome accepte le/plain, mais IE ne le fait pas, quelle que soit la version que j'ai essayée.

Je me suis débattu avec le même problème pendant plusieurs semaines, essayant de comprendre pourquoi mes événements de "chute" ne se déroulaient pas correctement dans IE alors qu'ils le faisaient dans Chrome. C'était parce que les données dataTransfer n'avaient pas été chargées correctement.

12
Roland

Je sais que vous avez déjà répondu à cette question, mais c'est un fil utile - je voulais juste ajouter un additif ici - si vous définissez les données vous-même, vous pouvez toujours ajouter les données dans le champ même (moche je sais), mais cela évite de devoir recréer des fonctionnalités:

Par exemple, si vous définissez vos propres données personnalisées:

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

annexer les données en tant que nouvelle entrée de données et effectuer une itération:

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

interroger:

// 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
        }
    }
}

testé en chrome seulement

5
ansiart

Il convient également de noter que si vous quittez la chaîne d’exécution en utilisant un délai d’expiration, l’objet dataTransfer ne contiendra plus vos données .

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

Je suis tombé sur ce message parce que j'avais une expérience similaire avec les fonctions dataTransfer.setData() et dataTransfer.getData() de Chrome.

J'avais un code qui ressemblait à quelque chose comme ça:

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;
}

Le code fonctionnait parfaitement dans Internet Explorer, mais lorsqu’il a été testé dans Chrome, je n’ai pas pu définir les valeurs définies dans mon objet dataTransfer (défini dans la fonction glisser) à l’aide de la fonction dataTransfer.getData() dans la fonction newDrop. J'ai également été incapable d'obtenir la valeur id de l'instruction ev.target.id.

Après quelques recherches sur le Web, j'ai découvert que j'étais supposé utiliser la propriété des paramètres d'événement currentTarget plutôt que la propriété events target. Le code mis à jour ressemblait à ceci:

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;
}

Grâce à ce changement, j'ai pu utiliser les fonctions dataTransfer.setData() et dataTransfer.getData() en chrome ainsi que dans Internet Explorer. Je n'ai testé nulle part ailleurs et je ne sais pas pourquoi cela a fonctionné. J'espère que cela vous aide et j'espère que quelqu'un pourra donner une explication.

1
Joshua Espana

J'obtenais la même erreur pour le code ci-dessous:

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

J'ai changé le code en: 

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

Et cela a fonctionné pour moi.

1
RoshanZ