Zapobiegaj ładowaniu przez przeglądarkę przeciąganego i upuszczanego pliku

194

Do mojej strony dodaję narzędzie do przesyłania i przeciągania HTML5.

Po upuszczeniu pliku do obszaru przesyłania wszystko działa świetnie.

Jeśli jednak przypadkowo upuszczę plik poza obszar przesyłania, przeglądarka ładuje plik lokalny tak, jakby to była nowa strona.

Jak mogę temu zapobiec?

Dzięki!

Travis
źródło
2
Ciekawe, jakiego kodu używasz do obsługi przesyłania metodą przeciągnij / upuść HTML5. Dzięki.
robertwbradford
Przyczyną problemu jest brak e.dataTransfer () lub brak funkcji preventDefault () w drop / dragenter / etc. wydarzenia Ale nie mogę powiedzieć bez próbki kodu.
HoldOffHunger

Odpowiedzi:

314

Możesz dodać detektor zdarzeń do okna, które wywołuje preventDefault()wszystkie zdarzenia przeciągania i upuszczania.
Przykład:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);
Samolot cyfrowy
źródło
45
dragover to kawałek, którego mi brakowało.
cgatian
11
Potwierdzam, że oba dragoveri dropprogramy obsługi są potrzebne, aby przeglądarka nie załadowała upuszczonego pliku. (Chrome najnowszy 2015/08/03). Rozwiązanie działa również w najnowszej wersji FF.
Offirmo
4
Działa to doskonale i mogę potwierdzić, że można go używać w połączeniu z elementami strony, które są skonfigurowane do przyjmowania zdarzeń upuszczania, takich jak te z skryptów przesyłania plików metodą przeciągnij i upuść, takich jak resumable.js. Przydatne jest zapobieganie domyślnemu zachowaniu przeglądarki w przypadkach, gdy użytkownik przypadkowo upuści plik, który chce przesłać poza rzeczywistą strefę upuszczania plików, a następnie zastanawia się, dlaczego teraz widzi ten sam plik renderowany bezpośrednio w oknie przeglądarki ( zakładając, że został usunięty zgodny typ pliku, taki jak obraz lub film), a nie oczekiwane zachowanie po przesłaniu pliku.
bluebinary
15
Uwaga: wyłącza to także przeciąganie plików do pliku <input type="file" />. Konieczne jest sprawdzenie, czy e.targetjest to plik wejściowy i przepuszczenie takich zdarzeń.
Sebastian Nowak,
6
co ? dlaczego przeciąganie okien powinno ładować plik? to nie ma sensu ...
L.Trabacchin
38

Po wielu zabawach odkryłem, że jest to najbardziej stabilne rozwiązanie:

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>

Ustawienie obu effectAllowi dropEffectbezwarunkowo w oknie powoduje, że moja strefa zrzutu nie akceptuje już dnd, niezależnie od tego, czy właściwości są ustawione jako nowe czy nie.

Axel Amthor
źródło
e.dataTransfer () jest tutaj kluczowym elementem, który sprawia, że ​​działa, o którym „wzmiankowana odpowiedź” nie wspomniała.
HoldOffHunger
9

W przypadku jQuery poprawną odpowiedzią będzie:

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

Tutaj return falsebędzie się zachowywać jak event.preventDefault()i event.stopPropagation().

Wizja
źródło
9

Aby zezwolić na przeciąganie i upuszczanie tylko niektórych elementów, możesz zrobić coś takiego:

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);
entuzjastycznie
źródło
Działa idealnie dla mnie, ale dodałbym również sprawdzanie typu = plik, w przeciwnym razie nadal można przeciągać do wprowadzania tekstu
Andreas Zwerger
2

Spróbuj tego:

document.body.addEventListener('drop', function(e) {
    e.preventDefault();
}, false);
moe
źródło
2

Domyślnie zapobieganie wszystkim operacjom przeciągania i upuszczania może być inne. Możliwe jest sprawdzenie, czy źródło przeciągania jest plikiem zewnętrznym, przynajmniej w niektórych przeglądarkach. W tej odpowiedzi StackOverflow umieściłem funkcję sprawdzania, czy źródło przeciągania jest plikiem zewnętrznym .

Zmieniając odpowiedź Digital Plane, możesz zrobić coś takiego:

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);
Shannon Matthews
źródło
1
Jaki jest sens e || event;? Gdzie jest eventzdefiniowane? Nieważne. Wygląda na to, że jest to obiekt globalny w IE? Znalazłem ten cytat "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." tutaj
1,21 gigawatów
2

Uwaga: Chociaż OP nie poprosił o rozwiązanie Angular, przyszedłem tutaj, aby tego szukać. Tak więc podzielę się tym, co uważam za realne rozwiązanie, jeśli używasz Angulara.

Z mojego doświadczenia wynika, że ​​problem ten pojawia się po raz pierwszy po dodaniu funkcji upuszczania plików na stronie. Dlatego uważam, że składnik, który to dodaje, powinien również odpowiadać za zapobieganie wypadaniu poza strefę zrzutu.

W moim rozwiązaniu strefa zrzutu jest wejściem z klasą, ale każdy jednoznaczny selektor działa.

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

Detektory są dodawane / usuwane automatycznie, gdy komponent jest tworzony / niszczony, a inne komponenty korzystające z tej samej strategii na tej samej stronie nie kolidują ze sobą z powodu stopPropagation ().

Superole
źródło
To działa jak urok !! Przeglądarka nawet zmienia kursor myszy, dodając ikonę banowania, która jest tak świetna !!
pti_jul
1

Aby skorzystać z metody „sprawdź cel” opisanej w kilku innych odpowiedziach, oto bardziej ogólna / funkcjonalna metoda:

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

Nazywany jak:

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])
);
scott_trinh
źródło
Ponadto, można dodać trochę ES6 tutaj: function preventDefaultExcept(...predicates){}. A potem użyj go jakpreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn
0

Mam HTML object( embed), który wypełnia szerokość i wysokość strony. Odpowiedź @ digital-plane działa na zwykłych stronach internetowych, ale nie, jeśli użytkownik wpadnie na osadzony obiekt. Potrzebowałem więc innego rozwiązania.

Jeśli przejdziemy do fazy przechwytywania zdarzeń , możemy uzyskać zdarzenia, zanim obiekt osadzony je odbierze (zwróć uwagę na truewartość na końcu wywołania detektora zdarzeń):

// 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);

Korzystając z następującego kodu (na podstawie odpowiedzi @ digital-plane) strona staje się celem przeciągania, zapobiega osadzaniu obiektów przez obiekty, a następnie ładuje nasze obrazy:

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

Testowane na Firefox na Macu.

1,21 gigawata
źródło
0

Używam selektora klasy do wielu obszarów przesyłania, więc moje rozwiązanie przybrało mniej czystą formę

Na podstawie odpowiedzi Axela Amthora, w zależności od jQuery (alias do $)

_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);
        });
    },
hngr18
źródło