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