วิธีลากและวางไดเรกทอรี

อินเทอร์เฟซการลากและวางแบบ HTML ช่วยให้เว็บแอปพลิเคชันยอมรับไฟล์ที่ลากและวางบนหน้าเว็บได้ ระหว่างการดำเนินการลากและวาง ไฟล์ที่ลากและรายการไดเรกทอรีจะเชื่อมโยงกับรายการไฟล์และรายการไดเรกทอรีตามลำดับ เมื่อพูดถึงการลากและวางไฟล์ลงในเบราว์เซอร์ มี 2 วิธีในการดำเนินการ ได้แก่ แบบสมัยใหม่และคลาสสิก

เมธอด DataTransferItem.getAsFileSystemHandle() จะส่งกลับคำสัญญาพร้อมด้วยออบเจ็กต์ FileSystemFileHandle หากรายการที่ลากเป็นไฟล์ และคำสัญญาที่มีออบเจ็กต์ FileSystemDirectoryHandle หากรายการที่ลากเป็นไดเรกทอรี แฮนเดิลเหล่านี้ให้คุณอ่านและเลือกเขียนกลับไปยังไฟล์หรือไดเรกทอรีได้ โปรดทราบว่า DataTransferItem.kind ของอินเทอร์เฟซแบบลากและวางจะเป็น "file" สำหรับทั้งไฟล์และไดเรกทอรี ขณะที่ FileSystemHandle.kind ของ File System Access API จะเป็น "file" สำหรับไฟล์และ "directory" สำหรับไดเรกทอรี

การสนับสนุนเบราว์เซอร์

  • 86
  • 86
  • x
  • x

แหล่งที่มา

วิธีคลาสสิก

การใช้เมธอด DataTransferItem.webkitGetAsEntry() ที่ไม่เป็นมาตรฐาน

เมธอด DataTransferItem.webkitGetAsEntry() จะแสดงผล FileSystemFileEntry ของรายการข้อมูลการลากหากรายการนั้นเป็นไฟล์ และแสดงผล FileSystemDirectoryEntry หากรายการนั้นเป็นไดเรกทอรี แม้ว่าคุณจะอ่านไฟล์หรือไดเรกทอรีได้ แต่จะเขียนกลับไปกลับมาไม่ได้ วิธีนี้มีข้อเสียเปรียบเนื่องจากไม่ได้อยู่ในแทร็กมาตรฐาน แต่มีข้อดีตรงที่รองรับไดเรกทอรี

การสนับสนุนเบราว์เซอร์

  • 13
  • 14
  • 50
  • 11.1

แหล่งที่มา

การเพิ่มประสิทธิภาพแบบต่อเนื่อง

ข้อมูลโค้ดด้านล่างใช้เมธอด DataTransferItem.getAsFileSystemHandle() ของ File System Access API ที่ทันสมัยเมื่อระบบรองรับ จากนั้นจะกลับไปใช้เมธอด DataTransferItem.webkitGetAsEntry() ที่ไม่เป็นไปตามมาตรฐาน และสุดท้ายจะกลับไปใช้เมธอด DataTransferItem.getAsFile() แบบคลาสสิก โปรดตรวจสอบประเภทของ handle แต่ละรายการ เนื่องจากอาจเป็นอย่างใดอย่างหนึ่งต่อไปนี้

  • FileSystemDirectoryHandle เมื่อมีการเลือกเส้นทางโค้ดสมัยใหม่
  • FileSystemDirectoryEntry เมื่อเลือกเส้นทางโค้ดที่ไม่เป็นมาตรฐาน

ประเภททั้งหมดมีพร็อพเพอร์ตี้ name ดังนั้นการบันทึกจึงทำได้ดีและใช้งานได้เสมอ

// Run feature detection.
const supportsFileSystemAccessAPI =
 
'getAsFileSystemHandle' in DataTransferItem.prototype;
const supportsWebkitGetAsEntry =
 
'webkitGetAsEntry' in DataTransferItem.prototype;

// This is the drag and drop zone.
const elem = document.querySelector('main');

// Prevent navigation.
elem
.addEventListener('dragover', (e) => {
  e
.preventDefault();
});

// Visually highlight the drop zone.
elem
.addEventListener('dragenter', (e) => {
  elem
.style.outline = 'solid red 1px';
});

// Visually unhighlight the drop zone.
elem
.addEventListener('dragleave', (e) => {
  elem
.style.outline = '';
});

// This is where the drop is handled.
elem
.addEventListener('drop', async (e) => {
 
// Prevent navigation.
  e
.preventDefault();
 
if (!supportsFileSystemAccessAPI && !supportsWebkitGetAsEntry) {
   
// Cannot handle directories.
   
return;
 
}
 
// Unhighlight the drop zone.
  elem
.style.outline = '';

 
// Prepare an array of promises…
 
const fileHandlesPromises = [...e.dataTransfer.items]
   
// …by including only files (where file misleadingly means actual file _or_
   
// directory)…
   
.filter((item) => item.kind === 'file')
   
// …and, depending on previous feature detection…
   
.map((item) =>
      supportsFileSystemAccessAPI
       
// …either get a modern `FileSystemHandle`…
       
? item.getAsFileSystemHandle()
       
// …or a classic `FileSystemFileEntry`.
       
: item.webkitGetAsEntry(),
   
);
 
// Loop over the array of promises.
 
for await (const handle of fileHandlesPromises) {
   
// This is where we can actually exclusively act on the directories.
   
if (handle.kind === 'directory' || handle.isDirectory) {
      console
.log(`Directory: ${handle.name}`);
   
}
 
}
});

อ่านเพิ่มเติม

เดโม

<!DOCTYPE html>
<html lang="en">
 
<head>
   
<meta charset="utf-8" />
   
<meta name="viewport" content="width=device-width, initial-scale=1" />
   
<title>How to drag and drop directories</title>
 
</head>
 
<body>
   
<main>
     
<h1>How to drag and drop directories</h1>
     
<p>Drag and drop one or multiple files or directories onto the page.</p>
     
<pre></pre>
   
</main>
 
</body>
</html>

       
:root {
 
color-scheme: dark light;
 
box-sizing: border-box;
}

*,
*:before,
*:after {
 
box-sizing: inherit;
}

body
{
 
margin: 0;
 
padding: 1rem;
 
font-family: system-ui, sans-serif;
 
line-height: 1.5;
 
min-height: 100vh;
 
display: flex;
 
flex-direction: column;
}

img
,
video
{
 
height: auto;
 
max-width: 100%;
}

main
{
 
flex-grow: 1;
}

footer
{
 
margin-top: 1rem;
 
border-top: solid CanvasText 1px;
 
font-size: 0.8rem;
}
       

       
const supportsFileSystemAccessAPI =
 
"getAsFileSystemHandle" in DataTransferItem.prototype;
const supportsWebkitGetAsEntry =
 
"webkitGetAsEntry" in DataTransferItem.prototype;

const elem = document.querySelector("main");
const debug = document.querySelector("pre");

elem
.addEventListener("dragover", (e) => {
 
// Prevent navigation.
  e
.preventDefault();
});

elem
.addEventListener("dragenter", (e) => {
  elem
.style.outline = "solid red 1px";
});

elem
.addEventListener("dragleave", (e) => {
  elem
.style.outline = "";
});

elem
.addEventListener("drop", async (e) => {
  e
.preventDefault();
  elem
.style.outline = "";
 
const fileHandlesPromises = [...e.dataTransfer.items]
   
.filter((item) => item.kind === "file")
   
.map((item) =>
      supportsFileSystemAccessAPI
       
? item.getAsFileSystemHandle()
       
: supportsWebkitGetAsEntry
       
? item.webkitGetAsEntry()
       
: item.getAsFile()
   
);

 
for await (const handle of fileHandlesPromises) {
   
if (handle.kind === "directory" || handle.isDirectory) {
      console
.log(`Directory: ${handle.name}`);
      debug
.textContent += `Directory: ${handle.name}\n`;
   
} else {
      console
.log(`File: ${handle.name}`);
      debug
.textContent += `File: ${handle.name}\n`;
   
}
 
}
});