Запретить браузеру загрузку файла перетаскивания

194

Я добавляю html5 drag and drop uploader на мою страницу.

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

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

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

Спасибо!

Travis
источник
2
Просто любопытно, какой код вы используете для обработки перетаскивания html5. Спасибо.
Робертбрадфорд,
Проблема, которая у вас возникла, вызвана отсутствием e.dataTransfer () или предотвращением protectDefault () в drop / dragenter / etc. События. Но я не могу сказать без примера кода.
HoldOffHunger

Ответы:

314

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

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);
Цифровой самолет
источник
45
Dragover это кусок, который я пропустил.
cgatian
11
Я подтверждаю, что оба dragoverи dropобработчики необходимы, чтобы браузер не загружал удаленный файл. (Chrome последний 2015/08/03). Решение работает и на FF последней.
Offirmo
4
Это прекрасно работает, и я могу подтвердить, что его можно использовать в сочетании с элементами страницы, которые настроены на прием событий перетаскивания, таких как из файлов перетаскивания с перетаскиванием файлов, таких как resumable.js. Полезно предотвратить поведение браузера по умолчанию в тех случаях, когда пользователь случайно удаляет файл, который он хочет выгрузить, вне фактической зоны перетаскивания файлов, а затем задается вопросом, почему он теперь видит этот же файл, отображаемый непосредственно в окне браузера ( предполагая, что совместимый тип файла, такой как изображение или видео, был удален), а не ожидаемое поведение при загрузке файла.
Bluebinary
15
Примечание: это также отключает перетаскивание файлов на <input type="file" />. Нужно проверить, e.targetесть ли файл ввода и пропустить такие события.
Себастьян Новак
6
что ? почему окно dragover загружает файл? в этом нет смысла ...
Л.Трабачин
38

После долгих раздумий я обнаружил, что это самое стабильное решение:

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>

Установка обоих effectAllowи dropEffectбезоговорочно в окне заставляет мою зону удаления больше не принимать dnd, независимо от того, установлены ли свойства новыми или нет.

Аксель Амтор
источник
e.dataTransfer () - критическая часть, которая делает эту работу, о которой «принятый ответ» не упомянул.
HoldOffHunger
9

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

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

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

зрение
источник
9

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

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);
enthus1ast
источник
Прекрасно работает для меня, но я бы также добавил проверку для type = file, в противном случае вы все равно можете перетаскивать на
ввод
2

попробуй это:

document.body.addEventListener('drop', function(e) {
    e.preventDefault();
}, false);
МОС
источник
2

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

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

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
Какой смысл e || event;? Где eventопределяется? Неважно. Похоже, это глобальный объект в IE? Я нашел эту цитату, "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." здесь
1.21 гигаватт
2

Примечание. Несмотря на то, что 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 ().

Superole
источник
Это работает как шарм! Браузер даже меняет курсор мыши, добавляя значок запрета, который так хорош !!
pti_jul
1

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

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])
);
scott_trinh
источник
Кроме того , можно добавить некоторые ES6 здесь: function preventDefaultExcept(...predicates){}. И затем используйте это какpreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn
0

У меня есть 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);

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

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.

1,21 гигаватт
источник
0

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

Основано на ответе Акселя Амтора с зависимостью от 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);
        });
    },
hngr18
источник