رابطهای کشیدن و رها کردن HTML، برنامههای کاربردی وب را قادر میسازد تا فایلهای کشیده و رها شده را در یک صفحه وب بپذیرند. در طول عملیات کشیدن و رها کردن، آیتم های فایل و دایرکتوری کشیده شده به ترتیب با ورودی های فایل و ورودی های دایرکتوری مرتبط می شوند. وقتی صحبت از کشیدن و رها کردن فایل ها به مرورگر می شود، دو راه برای انجام آن وجود دارد: روش مدرن و کلاسیک.
روش مدرن
با استفاده از روش File System Access API DataTransferItem.getAsFileSystemHandle()
متد DataTransferItem.getAsFileSystemHandle()
یک وعده را با یک شی FileSystemFileHandle
برمی گرداند اگر آیتم کشیده شده یک فایل باشد، و یک وعده با یک شی FileSystemDirectoryHandle
اگر مورد کشیده شده یک دایرکتوری باشد. این دستهها به شما امکان میدهند که بخوانید و به صورت اختیاری دوباره به فایل یا دایرکتوری بنویسید. توجه داشته باشید که DataTransferItem.kind
واسط Drag and Drop برای فایل ها و دایرکتوری ها "file"
خواهد بود، در حالی که FileSystemHandle.kind
API File System Access برای فایل ها "file"
و برای دایرکتوری ها "directory"
خواهد بود.
روش کلاسیک
با استفاده از متد غیر استاندارد DataTransferItem.webkitGetAsEntry()
متد DataTransferItem.webkitGetAsEntry()
FileSystemFileEntry
مورد داده را در صورتی که آیتم یک فایل باشد و FileSystemDirectoryEntry
برمی گرداند اگر آیتم دایرکتوری باشد. در حالی که می توانید فایل یا دایرکتوری را بخوانید، هیچ راهی برای نوشتن مجدد به آنها وجود ندارد. این روش دارای معایبی است که در مسیر استاندارد قرار ندارد، اما این مزیت را دارد که از دایرکتوری ها پشتیبانی می کند.
افزایش پیشرونده
قطعه زیر از روش مدرن File System Access API DataTransferItem.getAsFileSystemHandle()
استفاده می کند، سپس به روش غیر استاندارد 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`;
}
}
});