Забороніть браузеру завантажувати перетягнутий файл


194

Я додаю завантажувач перетягування html5 на свою сторінку.

Коли файл випадає в область завантаження, все чудово працює.

Однак якщо я випадково викину файл за межі області завантаження, браузер завантажує локальний файл так, ніби це нова сторінка.

Як я можу запобігти такій поведінці?

Дякую!


2
Просто цікаво, який код ви використовуєте для обробки завантаження з перетягуванням html5. Дякую.
robertwbradford

Проблема у вас викликана відсутнім e.dataTransfer () або відсутнім prevenDefault () у drop / dragenter / тощо. події. Але я не можу сказати без зразка коду.
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
драговер - це твір, який мені бракувало.
cgatian

11
Я підтверджую, що dragoverі dropобробники потрібні, щоб браузер не завантажував файл, що випав. (Chrome останній 2015/08/03). Рішення також працює і на FF.
Offirmo

4
Це прекрасно працює, і я можу підтвердити, що його можна використовувати в поєднанні з елементами сторінки, налаштованими приймати події падіння, наприклад, з скриптів завантаження файлів перетягування файлів, таких як resumable.js. Корисно запобігти поведінці браузера за замовчуванням у випадках, коли користувач випадково скидає файл, який він хоче завантажити за межі фактичної спадної зони завантаження файлів, а потім дивується, чому тепер вони бачать той самий файл, відображений безпосередньо у вікні браузера ( припускаючи, що сумісний тип файлу, як-от зображення або відео, було відхилено), а не очікувана поведінка при перегляді завантаження файлу.
bluebinary

15
Примітка. Це також вимикає перетягування файлів на <input type="file" />. Потрібно перевірити, чи e.targetє вхід файлу і пропустити такі події.
Себастьян Новак

6
що ? чому драйвер вікна повинен завантажувати файл? це не має сенсу ...
Л.Трабакчин

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

Для мене ідеально підходить, але я б також додав чек на тип = файл, інакше ви все одно можете перетягнути на введення тексту
Andreas Zwerger

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

Примітка: Хоча ОП не просила рішення у кутку, я прийшов сюди шукати цього. Отже, це поділитися тим, що я вважав життєздатним рішенням, якщо ви використовуєте 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 ().


Це працює як шарм !! Веб-переглядач навіть змінює курсор миші, додавши настільки чудовий значок заборони !!
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])
);

Крім того , можна додати деякі 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);

За допомогою наступного коду (на основі відповіді @ цифрової площини) сторінка стає ціллю перетягування, вона заважає вкладкам об'єктів фіксувати події, а потім завантажує наші зображення:

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.


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);
        });
    },
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.