Antarmuka Drag and Drop HTML memungkinkan aplikasi web menerima file yang ditarik dan dilepas di halaman web. Selama operasi drag and drop, item file dan direktori yang ditarik dikaitkan dengan entri file dan entri direktori. Saat menarik dan melepas file ke browser, ada dua cara untuk melakukannya: cara modern dan klasik.
Cara modern
Menggunakan metode DataTransferItem.getAsFileSystemHandle() File System Access API
Metode DataTransferItem.getAsFileSystemHandle() menampilkan promise dengan objek FileSystemFileHandle jika item yang ditarik adalah file, dan promise dengan objek FileSystemDirectoryHandle jika item yang ditarik adalah direktori. Dengan handle ini, Anda dapat membaca, dan secara opsional menulis kembali ke file atau direktori. Perhatikan bahwa Drag and Drop interface's
DataTransferItem.kind akan menjadi
"file" untuk file dan direktori, sedangkan File System Access API's
FileSystemHandle.kind akan
menjadi "file" untuk file dan "directory" untuk direktori.
Cara klasik
Menggunakan metode DataTransferItem.webkitGetAsEntry() non-standar
Metode DataTransferItem.webkitGetAsEntry() menampilkan FileSystemFileEntry item data drag jika item tersebut adalah file, dan FileSystemDirectoryEntry jika item tersebut adalah direktori. Meskipun Anda dapat membaca file atau direktori, tidak ada cara untuk menulis kembali ke file atau direktori tersebut. Metode ini memiliki kelemahan karena tidak berada di jalur standar, tetapi memiliki keunggulan karena mendukung direktori.
Progressive enhancement
Cuplikan di bawah menggunakan metode DataTransferItem.getAsFileSystemHandle() File System Access API modern jika didukung, lalu kembali ke metode DataTransferItem.webkitGetAsEntry() non-standar, dan terakhir kembali ke metode DataTransferItem.getAsFile() klasik. Pastikan untuk memeriksa jenis setiap handle, karena dapat berupa salah satu dari:
FileSystemDirectoryHandlesaat jalur kode modern dipilih.FileSystemDirectoryEntrysaat jalur kode non-standar dipilih.
Semua jenis memiliki properti name, sehingga mencatatnya tidak masalah dan akan selalu berfungsi.
// 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}`);
}
}
});
Bacaan lebih lanjut
Demo
HTML
<!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>CSS
: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;
}
JS
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`;
}
}
});