ป้องกันไม่ให้เบราว์เซอร์โหลดไฟล์แบบลากแล้ววาง


194

ฉันกำลังเพิ่มตัวอัปโหลดลากและวาง html5 ในหน้าของฉัน

เมื่อไฟล์ถูกทิ้งลงในพื้นที่อัพโหลดทุกอย่างใช้งานได้ดี

อย่างไรก็ตามหากฉันบังเอิญวางไฟล์นอกพื้นที่อัพโหลดเบราว์เซอร์จะโหลดไฟล์ในเครื่องเสมือนว่าเป็นหน้าใหม่

ฉันจะป้องกันพฤติกรรมนี้ได้อย่างไร

ขอบคุณ!


2
แค่อยากรู้ว่าคุณใช้รหัสอะไรเพื่อจัดการการอัปโหลดลาก / วาง html5 ขอบคุณ
robertwbradford

ปัญหาที่คุณมีเกิดจาก e.dataTransfer () ที่ขาดหายไปหรือหายไปจาก PreventDefault () ที่ drop / dragenter / etc เหตุการณ์ที่เกิดขึ้น แต่ฉันไม่สามารถบอกได้ถ้าไม่มีตัวอย่างรหัส
HoldOffHunger

คำตอบ:


314

คุณสามารถเพิ่ม listener เหตุการณ์ไปยังหน้าต่างที่เรียกpreventDefault()ใช้ dragover และ drop events ทั้งหมด
ตัวอย่าง:

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 หน้าต่างควรโหลดไฟล์? สิ่งนี้ไม่สมเหตุสมผล ...
L.Trabacchin

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

ทำงานได้สมบูรณ์แบบสำหรับฉัน แต่ฉันจะเพิ่มการตรวจสอบไฟล์ type = มิฉะนั้นคุณยังสามารถลากไปที่อินพุตข้อความได้
Andreas Zwerger


2

การป้องกันการลากและวางทั้งหมดโดยค่าเริ่มต้นอาจไม่ใช่สิ่งที่คุณต้องการ เป็นไปได้ที่จะตรวจสอบว่าแหล่งที่มาของการลากเป็นไฟล์ภายนอกหรือไม่ในเบราว์เซอร์บางตัว ฉันได้รวมฟังก์ชันเพื่อตรวจสอบว่าแหล่งที่มาของการลากเป็นไฟล์ภายนอกในคำตอบของ StackOverflowนี้หรือไม่

การแก้ไขคำตอบของเครื่องบินดิจิทัลคุณสามารถทำสิ่งนี้:

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 gigawatts

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


มันใช้งานได้เหมือนมนต์เสน่ห์ !! เบราว์เซอร์ยังเปลี่ยนเคอร์เซอร์เมาส์ด้วยการเพิ่มไอคอนแบนซึ่งยอดเยี่ยมมาก !!
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);

การใช้รหัสต่อไปนี้ (ขึ้นอยู่กับคำตอบของ @ 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


0

ฉันกำลังใช้ตัวเลือกคลาสสำหรับพื้นที่อัปโหลดหลายแห่งดังนั้นโซลูชันของฉันจึงใช้รูปแบบที่บริสุทธิ์น้อยกว่านี้

ตามคำตอบของ Axel Amthor โดยขึ้นอยู่กับ jQuery (aliased ถึง $)

_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);
        });
    },
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.