Ngăn trình duyệt tải tệp kéo và thả


194

Tôi đang thêm một trình kéo và thả html5 vào trang của mình.

Khi một tập tin được thả vào khu vực tải lên, mọi thứ đều hoạt động tốt.

Tuy nhiên, nếu tôi vô tình làm rơi tệp bên ngoài khu vực tải lên, trình duyệt sẽ tải tệp cục bộ như thể đó là một trang mới.

Làm thế nào tôi có thể ngăn chặn hành vi này?

Cảm ơn!


2
Chỉ cần tò mò xem bạn đang sử dụng mã nào để xử lý việc tải lên / kéo html5. Cảm ơn.
robertwbradford

Sự cố bạn gặp phải là do thiếu e.dataTransfer () hoặc thiếu một defDefault () khi thả / dragenter / v.v. sự kiện. Nhưng tôi không thể nói mà không có mẫu mã.
Hold OfferHunger

Câu trả lời:


312

Bạn có thể thêm một trình lắng nghe sự kiện vào cửa sổ gọi preventDefault()tất cả các sự kiện kéo và thả.
Thí dụ:

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

44
dragover là mảnh tôi đã mất.
cgatian

11
Tôi xác nhận rằng cả hai dragoverdroptrình xử lý là cần thiết để ngăn trình duyệt tải tệp bị rơi. (Chrome mới nhất 2015/08/03). Giải pháp hoạt động trên FF mới nhất, quá.
Offirmo

4
Điều này hoạt động hoàn hảo và tôi có thể xác nhận rằng nó có thể được sử dụng kết hợp với các thành phần trang được định cấu hình để chấp nhận các sự kiện thả, chẳng hạn như các sự kiện từ tập lệnh tải lên tập tin kéo và thả như resumable.js. Rất hữu ích để ngăn chặn hành vi mặc định của trình duyệt trong trường hợp người dùng vô tình làm rơi tệp mà họ muốn tải lên bên ngoài vùng thả tệp tải lên thực tế và sau đó tự hỏi tại sao bây giờ họ lại thấy tệp đó được hiển thị trực tiếp trong cửa sổ trình duyệt ( giả sử loại tệp tương thích như hình ảnh hoặc video bị loại bỏ), thay vì hành vi dự kiến ​​sẽ thấy tệp tải lên của họ.
bluebinary

15
Lưu ý: điều này cũng vô hiệu hóa việc kéo tập tin vào a <input type="file" />. Nó là cần thiết để kiểm tra xem e.targetlà một đầu vào tập tin và cho phép các sự kiện như vậy thông qua.
Sebastian Nowak

6
gì ? Tại sao cửa sổ kéo tải tập tin? điều này vô nghĩa ...
L.Trabacchin

37

Sau rất nhiều lần nghịch ngợm, tôi thấy đây là giải pháp ổn định nhất:

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>

Đặt cả hai effectAllowdropEffectvô điều kiện trên cửa sổ khiến vùng thả của tôi không chấp nhận bất kỳ dnd nào nữa, bất kể các thuộc tính có được đặt mới hay không.


e.dataTransfer () là phần quan trọng ở đây để thực hiện công việc này, điều mà "câu trả lời được chấp nhận" không đề cập đến.
Hold OfferHunger

9

Đối với jQuery, câu trả lời đúng sẽ là:

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

Ở đây return falsesẽ hành xử như event.preventDefault()event.stopPropagation().


9

Để chỉ cho phép kéo và thả trên một số thành phần, bạn có thể làm một số việc như:

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

Hoạt động hoàn hảo với tôi, nhưng tôi cũng sẽ thêm kiểm tra type = file, nếu không bạn vẫn có thể kéo vào văn bản nhập
Andreas Zwerger

2

thử cái này:

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

2

Ngăn chặn tất cả các hoạt động kéo và thả theo mặc định có thể không phải là điều bạn muốn. Có thể kiểm tra xem nguồn kéo có phải là tệp bên ngoài hay không, ít nhất là trong một số trình duyệt. Tôi đã bao gồm một hàm để kiểm tra xem nguồn kéo có phải là tệp bên ngoài trong câu trả lời StackOverflow này không .

Sửa đổi câu trả lời của Máy bay kỹ thuật số, bạn có thể làm một cái gì đó như thế này:

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
Điểm của là e || event;gì? Trường hợp được eventxác định? Đừng bận tâm. Có vẻ như đó là một đối tượng toàn cầu trong IE? Tôi tìm thấy trích dẫn này, "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." ở đây
1,21 gigawatt

2

Lưu ý: Mặc dù OP không yêu cầu giải pháp Angular, tôi đã đến đây để tìm kiếm giải pháp đó. Vì vậy, đây là để chia sẻ những gì tôi thấy là một giải pháp khả thi, nếu bạn sử dụng Angular.

Theo kinh nghiệm của tôi, vấn đề này lần đầu tiên phát sinh khi bạn thêm chức năng thả tập tin vào một trang. Do đó, ý kiến ​​của tôi là thành phần bổ sung này, cũng có trách nhiệm ngăn chặn sự rơi ra ngoài vùng thả.

Trong giải pháp của tôi, vùng thả là một đầu vào với một lớp, nhưng bất kỳ bộ chọn rõ ràng nào đều hoạt động.

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

Các trình nghe được thêm / xóa tự động khi thành phần được tạo / hủy và các thành phần khác sử dụng cùng một chiến lược trên cùng một trang không can thiệp lẫn nhau do stopPropagation ().


Công việc này như một cái duyên vậy !! Trình duyệt thậm chí thay đổi con trỏ chuột bằng cách thêm biểu tượng cấm rất tuyệt vời !!
pti_jul

1

Để xây dựng phương pháp "kiểm tra mục tiêu" được nêu trong một vài câu trả lời khác, đây là một phương pháp chung / chức năng hơn:

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

Được gọi là:

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

Ngoài ra, có thể thêm một số ES6 ở đây : function preventDefaultExcept(...predicates){}. Và sau đó sử dụng nó như thếpreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn

0

Tôi có một HTML object( embed) lấp đầy chiều rộng và chiều cao của trang. Câu trả lời của @ máy bay kỹ thuật số hoạt động trên các trang web bình thường nhưng không phải nếu người dùng rơi vào một đối tượng nhúng. Vì vậy, tôi cần một giải pháp khác.

Nếu chúng ta chuyển sang sử dụng giai đoạn chụp sự kiện, chúng ta có thể nhận được các sự kiện trước khi đối tượng nhúng nhận được chúng (chú ý truegiá trị ở cuối cuộc gọi người nghe sự kiện):

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

Sử dụng đoạn mã sau (dựa trên câu trả lời của @ máy bay kỹ thuật số), trang trở thành mục tiêu kéo, nó ngăn đối tượng nhúng nhúng để chụp các sự kiện và sau đó tải hình ảnh của chúng tôi:

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

Đã thử nghiệm trên Firefox trên Mac.


0

Tôi đang sử dụng một bộ chọn lớp cho nhiều khu vực tải lên, vì vậy giải pháp của tôi có dạng không thuần túy này

Dựa trên câu trả lời của Axel Amthor, với sự phụ thuộc vào jQuery (bí danh là $)

_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);
        });
    },
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.