Empêcher le navigateur de charger un fichier glisser-déposer


194

J'ajoute un téléchargeur html5 glisser-déposer sur ma page.

Lorsqu'un fichier est déposé dans la zone de téléchargement, tout fonctionne très bien.

Cependant, si je dépose accidentellement le fichier en dehors de la zone de téléchargement, le navigateur charge le fichier local comme s'il s'agissait d'une nouvelle page.

Comment puis-je éviter ce comportement?

Merci!


2
Juste curieux de savoir quel code vous utilisez pour gérer le téléchargement par glisser / déposer html5. Merci.
robertwbradford

Le problème que vous rencontrez est dû soit à l'absence de e.dataTransfer (), soit à l'absence d'un preventDefault () sur drop / dragenter / etc. événements. Mais je ne peux pas dire sans un exemple de code.
HoldOffHunger

Réponses:


314

Vous pouvez ajouter un écouteur d'événements à la fenêtre qui appelle preventDefault()tous les événements glisser-déposer.
Exemple:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);

45
dragover est la pièce qui me manquait.
cgatian

11
Je confirme que les deux dragoveret les dropgestionnaires sont nécessaires pour empêcher le navigateur de charger le fichier déposé. (Chrome au dernier 2015/08/03). La solution fonctionne également sur la dernière version de FF.
Offirmo

4
Cela fonctionne parfaitement et je peux confirmer qu'il peut être utilisé en combinaison avec des éléments de page configurés pour accepter les événements de dépôt, tels que ceux des scrips de téléchargement de fichiers par glisser-déposer comme resumable.js. Il est utile d'empêcher le comportement par défaut du navigateur dans les cas où un utilisateur supprime accidentellement un fichier qu'il souhaite télécharger en dehors de la zone de dépôt de téléchargement de fichier réelle, puis se demande pourquoi il voit maintenant ce même fichier rendu directement dans la fenêtre du navigateur ( en supposant qu'un type de fichier compatible comme une image ou une vidéo a été supprimé), plutôt que le comportement attendu de voir leur téléchargement de fichier.
bluebinary

15
Remarque: cela désactive également le glissement de fichiers sur un fichier <input type="file" />. Il est nécessaire de vérifier si e.targetun fichier est entré et de laisser passer de tels événements.
Sebastian Nowak

6
quoi ? pourquoi la fenêtre glisser-déposer chargerait-elle le fichier? cela n'a aucun sens ...
L.Trabacchin

38

Après beaucoup de bidouilles, j'ai trouvé que c'était la solution la plus stable:

var dropzoneId = "dropzone";

window.addEventListener("dragenter", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
}, false);

window.addEventListener("dragover", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
});

window.addEventListener("drop", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
});
<div id="dropzone">...</div>

La définition des deux effectAllowet dropEffectinconditionnellement sur la fenêtre fait que ma zone de dépôt n'accepte plus aucun dnd, que les propriétés soient définies comme nouvelles ou non.


e.dataTransfer () est la pièce critique ici qui fait ce travail, que la "réponse acceptée" a omis de mentionner.
HoldOffHunger

9

Pour jQuery, la bonne réponse sera:

$(document).on({
    dragover: function() {
        return false;
    },
    drop: function() {
        return false;
    }
});

Ici return falsese comportera comme event.preventDefault()et event.stopPropagation().


9

Pour autoriser le glisser-déposer uniquement sur certains éléments, vous pouvez faire quelque chose comme:

window.addEventListener("dragover",function(e){
  e = e || event;
  console.log(e);
  if (e.target.tagName != "INPUT") { // check which element is our target
    e.preventDefault();
  }
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  console.log(e);
  if (e.target.tagName != "INPUT") {  // check which element is our target
    e.preventDefault();
  }  
},false);

Fonctionne parfaitement pour moi, mais j'ajouterais également vérifier le type = fichier, sinon vous pouvez toujours faire glisser vers les entrées de texte
Andreas Zwerger

2

essaye ça:

document.body.addEventListener('drop', function(e) {
    e.preventDefault();
}, false);

2

Empêcher toutes les opérations de glisser-déposer par défaut n'est peut-être pas ce que vous souhaitez. Il est possible de vérifier si la source de glissement est un fichier externe, au moins dans certains navigateurs. J'ai inclus une fonction pour vérifier si la source de glissement est un fichier externe dans ce réponse StackOverflow .

En modifiant la réponse de Digital Plane, vous pouvez faire quelque chose comme ceci:

function isDragSourceExternalFile() {
     // Defined here: 
     // https://stackoverflow.com/a/32044172/395461
}

window.addEventListener("dragover",function(e){
    e = e || event;
    var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
    if (IsFile) e.preventDefault();
},false);
window.addEventListener("drop",function(e){
    e = e || event;
    var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
    if (IsFile) e.preventDefault();
},false);

1
À quoi ça sert e || event;? Où est eventdéfini? Ça ne fait rien. On dirait que c'est un objet global dans IE? J'ai trouvé cette citation, "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." ici
1,21 gigawatts le

2

Remarque: bien que l'OP n'ait pas demandé de solution angulaire, je suis venu ici à la recherche de cela. Il s'agit donc de partager ce que j'ai trouvé être une solution viable, si vous utilisez Angular.

D'après mon expérience, ce problème survient pour la première fois lorsque vous ajoutez une fonctionnalité de suppression de fichier à une page. Par conséquent, mon avis est que le composant qui ajoute cela devrait également être responsable de la prévention des chutes en dehors de la zone de largage.

Dans ma solution, la zone de dépôt est une entrée avec une classe, mais tout sélecteur non ambigu fonctionne.

import { Component, HostListener } from '@angular/core';
//...

@Component({
  template: `
    <form>
      <!-- ... -->
      <input type="file" class="dropzone" />
    </form>
  `
})
export class MyComponentWithDropTarget {

  //...

  @HostListener('document:dragover', ['$event'])
  @HostListener('drop', ['$event'])
  onDragDropFileVerifyZone(event) {
    if (event.target.matches('input.dropzone')) {
      // In drop zone. I don't want listeners later in event-chain to meddle in here
      event.stopPropagation();
    } else {
      // Outside of drop zone! Prevent default action, and do not show copy/move icon
      event.preventDefault();
      event.dataTransfer.effectAllowed = 'none';
      event.dataTransfer.dropEffect = 'none';
    }
  }
}

Les écouteurs sont ajoutés / supprimés automatiquement lorsque le composant est créé / détruit, et les autres composants utilisant la même stratégie sur la même page n'interfèrent pas les uns avec les autres en raison de stopPropagation ().


Ça fonctionne super bien !! Le navigateur change même le curseur de la souris en ajoutant une icône d'interdiction, ce qui est génial !!
pti_jul

1

Pour construire sur la méthode «vérifier la cible» décrite dans quelques autres réponses, voici une méthode plus générique / fonctionnelle:

function preventDefaultExcept(predicates) {
  return function (e) {
    var passEvery = predicates.every(function (predicate) { return predicate(e); })
    if (!passEvery) {
      e.preventDefault();
    }
  };
}

Appelé comme:

function isDropzone(e) { return e.target.id === 'dropzone'; }
function isntParagraph(e) { return e.target.tagName !== 'p'; }

window.addEventListener(
  'dragover',
  preventDefaultExcept([isDropzone, isntParagraph])
);
window.addEventListener(
  'drop',
  preventDefaultExcept([isDropzone])
);

, Pourrait ajouter aussi quelques ES6 ici: function preventDefaultExcept(...predicates){}. Et puis utilisez-le commepreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn

0

J'ai un HTML object(embed ) qui remplit la largeur et la hauteur de la page. La réponse de @ digital-plane fonctionne sur des pages Web normales, mais pas si l'utilisateur tombe sur un objet incorporé. J'avais donc besoin d'une solution différente.

Si nous passons à l'utilisation de la phase de capture d'événements, nous pouvons obtenir les événements avant que l'objet intégré ne les reçoive (notez la truevaleur à la fin de l'appel de l'écouteur d'événements):

// document.body or window
document.body.addEventListener("dragover", function(e){
  e = e || event;
  e.preventDefault();
  console.log("over true");
}, true);

document.body.addEventListener("drop", function(e){
  e = e || event;
  e.preventDefault();
  console.log("drop true");
}, true);

En utilisant le code suivant (basé sur la réponse de @ digital-plane), la page devient une cible de glissement, elle empêche les incorporations d'objets de capturer les événements, puis charge nos images:

document.body.addEventListener("dragover", function(e){
  e = e || event;
  e.preventDefault();
  console.log("over true");
}, true);

document.body.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
  console.log("Drop true");

  // begin loading image data to pass to our embed
  var droppedFiles = e.dataTransfer.files;
  var fileReaders = {};
  var files = {};
  var reader;

  for (var i = 0; i < droppedFiles.length; i++) {
    files[i] = droppedFiles[i]; // bc file is ref is overwritten
    console.log("File: " + files[i].name + " " + files[i].size);
    reader = new FileReader();
    reader.file = files[i]; // bc loadend event has no file ref

    reader.addEventListener("loadend", function (ev, loadedFile) {
      var fileObject = {};
      var currentReader = ev.target;

      loadedFile = currentReader.file;
      console.log("File loaded:" + loadedFile.name);
      fileObject.dataURI = currentReader.result;
      fileObject.name = loadedFile.name;
      fileObject.type = loadedFile.type;
      // call function on embed and pass file object
    });

    reader.readAsDataURL(files[i]);
  }

}, true);

Testé sur Firefox sur Mac.


0

J'utilise un sélecteur de classe pour plusieurs zones de téléchargement, donc ma solution a pris cette forme moins pure

Basé sur la réponse d'Axel Amthor, avec une dépendance sur jQuery (alias $)

_stopBrowserFromOpeningDragAndDropPDFFiles = function () {

        _preventDND = function(e) {
            if (!$(e.target).is($(_uploadBoxSelector))) {
                e.preventDefault();
                e.dataTransfer.effectAllowed = 'none';
                e.dataTransfer.dropEffect = 'none';
            }
        };

        window.addEventListener('dragenter', function (e) {
            _preventDND(e);
        }, false);

        window.addEventListener('dragover', function (e) {
            _preventDND(e);
        });

        window.addEventListener('drop', function (e) {
            _preventDND(e);
        });
    },
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.