Предотвращение загрузки браузером перетаскиваемого файла

Я добавляю на мою страницу загружаемый файл html5.

Когда файл загружается в область загрузки, все работает отлично.

Однако, если я случайно удалю файл за пределами области загрузки, браузер загрузит локальный файл, как будто это новая страница.

Как я могу предотвратить это поведение?

Спасибо!

Ответ 1

Вы можете добавить прослушиватель событий в окно, которое вызывает preventDefault() во всех событиях перетаскивания и удаления.
Пример:

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

Ответ 2

После многих попыток, я нашел это самым стабильным решением:

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>

Ответ 3

Чтобы разрешить перетаскивание только для некоторых элементов, вы можете сделать что-то вроде:

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

Ответ 4

Для jQuery правильный ответ будет:

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

Здесь return false будет вести себя как event.preventDefault() и event.stopPropagation().

Ответ 5

попробуйте следующее:

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

Ответ 6

Предотвращение всех операций перетаскивания по умолчанию может быть не таким, каким вы хотите. Можно проверить, является ли источник перетаскивания внешним файлом, по крайней мере, в некоторых браузерах. Я включил функцию проверки, является ли источник перетаскивания внешним файлом в этом fooobar.com/questions/51995/....

Изменив ответ Digital Plane, вы можете сделать что-то вроде этого:

function isDragSourceExternalFile() {
     // Defined here: 
     // /questions/51995/how-do-i-detect-a-file-is-being-dragged-rather-than-a-draggable-element-on-my-page/366658#366658
}

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

Ответ 7

Чтобы опираться на метод "проверить цель", описанный в нескольких других ответах, вот более общий/функциональный метод:

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

Вызывается как:

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

Ответ 8

У меня есть HTML object (embed), который заполняет ширину и высоту страницы. Ответ на @digital-plane работает на обычных веб-страницах, но нет, если пользователь падает на внедренный объект. Поэтому мне нужно другое решение.

Если мы переключимся на фазу сбора событий, мы можем получить события до того, как внедренный объект их получит (обратите внимание на значение true в конце вызова слушателя события):

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

Используя следующий код (на основе ответа на цифровую плоскость), страница становится целью перетаскивания, она предотвращает захват объектов в результате событий, а затем загружает наши изображения:

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

Протестировано в Firefox на Mac.

Ответ 9

Я использую селектор классов для нескольких областей загрузки, поэтому мое решение приняло этот менее чистый вид

Основано на ответе Акселя Амтора с зависимостью от jQuery (с псевдонимом $)

_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);
        });
    },

Ответ 10

  Примечание: Несмотря на то, что OP не запрашивал угловое решение, я пришел сюда в поисках этого. Поэтому я хочу поделиться тем, что я считаю жизнеспособным решением, если вы используете Angular.

По моему опыту, эта проблема впервые возникает, когда вы добавляете функцию удаления файла на страницу. Поэтому я считаю, что компонент, который добавляет это, также должен отвечать за предотвращение выпадения за пределы зоны отбрасывания.

В моем решении зона сброса является входом с классом, но любой однозначный селектор работает.

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

Слушатели добавляются/удаляются автоматически при создании/уничтожении компонента, а другие компоненты, использующие одну и ту же стратегию на одной странице, не мешают друг другу из-за stopPropagation().