آکادمی بنو/آموزش HTML

Drag & Drop در HTML5

بهنام نوبخت · ۱۴۰۵/۶/۲۸

یکی از قابلیت‌های جذاب در صفحات وب، امکان کشیدن و رها کردن عناصر یا همان Drag & Drop است. احتمالاً در بسیاری از سایت‌ها دیده‌اید که می‌توانید یک فایل را روی یک ناحیه مشخص بکشید و رها کنید، یک محصول را به سبد خرید منتقل کنید یا یک وظیفه را از یک ستون به ستون دیگر ببرید. HTML5 امکاناتی برای پیاده‌سازی این رفتار در اختیار ما قرار می‌دهد و JavaScript نیز کنترل بیشتری روی آن ایجاد می‌کند. در این مقاله ابتدا با مفهوم Drag & Drop آشنا می‌شویم، سپس رویدادهای مربوط به آن، draggable، شیء DataTransfer و روش انتقال اطلاعات را بررسی می‌کنیم و در نهایت یک نمونه کاربردی شبیه یک برد مدیریت وظایف ایجاد خواهیم کرد.

این مقاله را بعد از یادگیری جاوا اسکریپت مطالعه کنید.

Drag & Drop چیست؟

Drag & Drop به معنی کشیدن و رها کردن است.

در یک عملیات معمول Drag & Drop سه بخش اصلی داریم:

  1. عنصری که قرار است کشیده شود

  2. اطلاعاتی که هنگام عملیات منتقل می‌شود

  3. ناحیه‌ای که عنصر یا اطلاعات روی آن رها می‌شود

برای مثال تصور کنید دو بخش داریم:

┌─────────────────┐
│   آیتم قابل    
│     کشیدن       
└─────────────────┘

          ↓

┌─────────────────────────┐
│      ناحیه رها کردن    │
│                         │
└─────────────────────────┘

کاربر آیتم اول را می‌گیرد، آن را حرکت می‌دهد و داخل ناحیه دوم رها می‌کند.

API مربوط به Drag & Drop در مرورگر بر پایه رویدادها کار می‌کند و برای عملیات مختلف رویدادهای متفاوتی در اختیار ما قرار می‌دهد.

Drag & Drop چه کاربردهایی دارد؟

از Drag & Drop می‌توان در بخش‌های مختلف یک وب‌سایت استفاده کرد.

برای مثال:

  • انتقال محصولات به سبد خرید

  • مرتب‌سازی آیتم‌ها

  • ساخت Kanban Board

  • انتقال وظایف بین وضعیت‌های مختلف

  • جابه‌جایی عناصر یک لیست

  • انتخاب و انتقال فایل

  • ساخت رابط‌های کاربری تعاملی

  • انتقال اطلاعات بین قسمت‌های مختلف صفحه

مثلاً در یک سیستم مدیریت پروژه می‌توانیم چنین ساختاری داشته باشیم:

انجام نشده       در حال انجام       انجام شده
──────────       ───────────       ─────────

طراحی صفحه       اتصال API         تست نهایی
ثبت‌نام           طراحی دیتابیس     انتشار

کاربر می‌تواند یک وظیفه را با ماوس بگیرد و آن را به ستون دیگری منتقل کند.

فعال کردن Drag برای یک عنصر

برای اینکه یک عنصر HTML بتواند به‌صورت Drag & Drop کشیده شود، می‌توانیم از ویژگی draggable استفاده کنیم.

مثال:

<div draggable="true">
    این عنصر را بکشید
</div>

مقدار:

draggable="true"

به مرورگر اعلام می‌کند که این عنصر قابلیت Drag شدن دارد.

مثلاً:

<div id="box" draggable="true">
    آیتم قابل جابه‌جایی
</div>

حالا این عنصر می‌تواند وارد یک عملیات Drag شود.

draggable چه مقادیری دارد؟

به‌صورت ساده می‌توانیم از این مقادیر استفاده کنیم:

draggable="true"

یا:

draggable="false"

در مثال‌های آموزشی ما معمولاً از true استفاده خواهیم کرد.

رویدادهای Drag & Drop

Drag & Drop به مجموعه‌ای از رویدادهای JavaScript متکی است.

مهم‌ترین رویدادها عبارت‌اند از:

رویداد

زمان اجرا

dragstart

شروع کشیدن عنصر

drag

هنگام کشیده شدن عنصر

dragend

پایان عملیات Drag

dragenter

ورود عنصر به یک ناحیه

dragover

قرار گرفتن عنصر روی ناحیه

dragleave

خارج شدن عنصر از ناحیه

drop

رها کردن عنصر

مهم‌ترین رویدادهایی که در پروژه‌های معمول بیشتر استفاده می‌کنیم عبارت‌اند از:

dragstart
dragover
drop
dragend

رویداد dragstart

این رویداد زمانی اجرا می‌شود که کاربر شروع به کشیدن عنصر می‌کند.

مثال:

<div id="box" draggable="true">
    آیتم من
</div>

<script>
    const box = document.getElementById("box");

    box.addEventListener("dragstart", function () {
        console.log("Drag شروع شد");
    });
</script>

وقتی کاربر شروع به کشیدن box کند، پیام زیر در Console نمایش داده می‌شود:

Drag شروع شد

dragstart یکی از مهم‌ترین محل‌ها برای آماده‌سازی اطلاعاتی است که قرار است در عملیات Drag منتقل شود.

رویداد drag

این رویداد در طول عملیات کشیدن عنصر اجرا می‌شود.

box.addEventListener("drag", function () {
    console.log("در حال کشیدن...");
});

این رویداد می‌تواند چندین بار در طول عملیات اجرا شود.

در بیشتر پروژه‌های ساده معمولاً نیازی نیست مستقیماً از آن استفاده کنیم.

رویداد dragend

وقتی عملیات Drag تمام شود، رویداد dragend اجرا می‌شود.

box.addEventListener("dragend", function () {
    console.log("Drag تمام شد");
});

نکته مهم این است که dragend در پایان عملیات اجرا می‌شود؛ چه عملیات Drop موفق باشد و چه موفق نباشد.

از این رویداد می‌توان برای:

  • حذف کلاس CSS مربوط به حالت Drag

  • پاک‌سازی وضعیت

  • بازگرداندن ظاهر عنصر

استفاده کرد.

Drop Target چیست؟

عنصری که قرار است آیتم را روی آن رها کنیم، Drop Target نام دارد.

مثلاً:

<div id="drop-zone">
    آیتم را اینجا رها کنید
</div>

اما یک نکته بسیار مهم وجود دارد.

به‌صورت پیش‌فرض، بیشتر عناصر HTML محل معتبر برای Drop نیستند.

برای اینکه یک عنصر بتواند Drop را قبول کند، معمولاً باید رویداد dragover را لغو کنیم:

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

این یکی از مهم‌ترین نکات Drag & Drop در HTML است. بدون این کار، معمولاً رویداد drop برای Drop Target اجرا نمی‌شود.

رویداد dragover

این رویداد زمانی اجرا می‌شود که عنصر در حال Drag روی یک ناحیه قرار دارد.

مثال:

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

خط زیر بسیار مهم است:

event.preventDefault();

با این کار به مرورگر اعلام می‌کنیم که این ناحیه می‌تواند Drop Target باشد.

رویداد dragenter

وقتی عنصر Drag شده وارد یک ناحیه شود، dragenter اجرا می‌شود.

dropZone.addEventListener("dragenter", function () {
    console.log("عنصر وارد ناحیه شد");
});

می‌توانیم از آن برای تغییر ظاهر ناحیه استفاده کنیم.

مثلاً:

dropZone.addEventListener("dragenter", function () {
    dropZone.classList.add("active");
});

رویداد dragleave

وقتی عنصر از ناحیه خارج شود، dragleave اجرا می‌شود.

dropZone.addEventListener("dragleave", function () {
    dropZone.classList.remove("active");
});

ترکیب dragenter و dragleave برای نمایش بازخورد بصری بسیار کاربردی است.

رویداد drop

مهم‌ترین رویداد Drop، رویداد drop است.

مثال:

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    console.log("عنصر رها شد");
});

ترتیب کلی عملیات را می‌توان این‌گونه تصور کرد:

dragstart
    ↓
drag
    ↓
dragenter
    ↓
dragover
    ↓
drop
    ↓
dragend

البته بسته به ساختار صفحه و نحوه حرکت عنصر، بعضی رویدادها ممکن است چندین بار اجرا شوند.

ساخت اولین مثال Drag & Drop

حالا یک مثال کامل بسازیم.

در این مثال یک مربع داریم و یک ناحیه که می‌توانیم مربع را داخل آن رها کنیم.

HTML

<div id="box" draggable="true">
    مرا بکش
</div>

<div id="drop-zone">
    اینجا رها کن
</div>

JavaScript

const box = document.getElementById("box");
const dropZone = document.getElementById("drop-zone");

box.addEventListener("dragstart", function () {
    console.log("شروع Drag");
});

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    console.log("Drop انجام شد");
});

در این مرحله هنوز عنصر را جابه‌جا نکرده‌ایم؛ فقط عملیات Drag و Drop را تشخیص داده‌ایم.

انتقال اطلاعات با DataTransfer

حالا به یکی از مهم‌ترین قسمت‌های Drag & Drop می‌رسیم.

شیء:

event.dataTransfer

اطلاعات مربوط به عملیات Drag را در اختیار ما قرار می‌دهد.

این شیء از طریق DataTransfer مدیریت می‌شود و می‌تواند اطلاعات متنی و همچنین فایل‌ها را در خود داشته باشد.

برای انتقال اطلاعات معمولاً از دو متد استفاده می‌کنیم:

setData()

و:

getData()

setData()

با setData() اطلاعاتی را برای عملیات Drag ذخیره می‌کنیم.

مثال:

box.addEventListener("dragstart", function (event) {
    event.dataTransfer.setData(
        "text/plain",
        "سلام از طرف عنصر Drag شده!"
    );
});

در اینجا:

"text/plain"

نوع داده است.

و:

"سلام از طرف عنصر Drag شده!"

اطلاعاتی است که منتقل می‌شود.

getData()

در زمان Drop می‌توانیم اطلاعات را دریافت کنیم:

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    const data = event.dataTransfer.getData("text/plain");

    console.log(data);
});

نتیجه:

سلام از طرف عنصر Drag شده!

نکته مهم این است که در عملیات استاندارد Drag & Drop، داده Drag شده را در رویداد dragstart تنظیم می‌کنیم و در زمان drop آن را می‌خوانیم.

مثال انتقال ID یک عنصر

در پروژه‌های واقعی معمولاً به‌جای انتقال یک متن ساده، می‌توانیم شناسه عنصر را منتقل کنیم.

HTML:

<div id="item1" draggable="true">
    محصول شماره 1
</div>

<div id="drop-zone">
    محصول را اینجا قرار دهید
</div>

JavaScript:

const item = document.getElementById("item1");
const dropZone = document.getElementById("drop-zone");

item.addEventListener("dragstart", function (event) {
    event.dataTransfer.setData("text/plain", item.id);
});

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    const itemId = event.dataTransfer.getData("text/plain");

    const draggedItem = document.getElementById(itemId);

    dropZone.appendChild(draggedItem);
});

حالا وقتی آیتم را روی Drop Zone رها کنیم، خود عنصر به داخل Drop Zone منتقل می‌شود.


انتقال عنصر یا انتقال اطلاعات؟

این دو مفهوم را باید از هم جدا کنیم.

در Drag & Drop ممکن است:

حالت اول: انتقال اطلاعات

مثلاً:

محصول شماره 10

را منتقل کنیم.

حالت دوم: انتقال خود عنصر

مثلاً:

<div id="product-10">
    محصول شماره 10
</div>

را از یک بخش DOM به بخش دیگری منتقل کنیم.

در مثال قبلی، ما ID عنصر را انتقال دادیم و سپس با JavaScript خود عنصر را جابه‌جا کردیم:

dropZone.appendChild(draggedItem);

پس DataTransfer الزاماً خود عنصر HTML را جابه‌جا نمی‌کند؛ می‌توانیم از اطلاعات منتقل‌شده برای پیدا کردن عنصر و سپس تغییر DOM استفاده کنیم.


effectAllowed چیست؟

ویژگی:

event.dataTransfer.effectAllowed

مشخص می‌کند که منبع Drag چه نوع عملیاتی را مجاز می‌داند.

برای مثال:

event.dataTransfer.effectAllowed = "move";

یعنی عملیات موردنظر از نوع جابه‌جایی است.

یا:

event.dataTransfer.effectAllowed = "copy";

یعنی عملیات از نوع کپی است.

مقادیر دیگری مانند:

none
copy
move
link
copyMove
copyLink
linkMove
all

نیز وجود دارند.

برای شروع فقط این دو مورد را به خاطر بسپارید:

"move"

و:

"copy"

dropEffect چیست؟

در Drop Target می‌توانیم با:

event.dataTransfer.dropEffect

مشخص کنیم عملیات Drop چه نوع اثری داشته باشد.

مثلاً:

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();

    event.dataTransfer.dropEffect = "move";
});

در اینجا هدف ما جابه‌جایی است.

یا:

event.dataTransfer.dropEffect = "copy";

برای حالت کپی استفاده می‌شود.

تفاوت ساده این دو را می‌توان این‌گونه در نظر گرفت:

effectAllowed
      ↓
چه عملیاتی از طرف منبع مجاز است؟

dropEffect
      ↓
در این Drop چه عملیاتی انجام شود؟

ایجاد ظاهر مناسب هنگام Drag

برای اینکه کاربر متوجه شود یک عنصر در حال Drag شدن است، می‌توانیم ظاهر آن را تغییر دهیم.

HTML:

<div id="box" draggable="true">
    آیتم من
</div>

<div id="drop-zone">
    اینجا رها کن
</div>

CSS:

#box {
    width: 150px;
    padding: 20px;
    background: orange;
    cursor: grab;
}

#drop-zone {
    width: 300px;
    height: 150px;
    margin-top: 30px;
    border: 2px dashed #999;
    display: flex;
    align-items: center;
    justify-content: center;
}

#drop-zone.active {
    border-color: green;
    background: #e8f5e9;
}

JavaScript:

const box = document.getElementById("box");
const dropZone = document.getElementById("drop-zone");

box.addEventListener("dragstart", function () {
    box.classList.add("dragging");
});

box.addEventListener("dragend", function () {
    box.classList.remove("dragging");
});

dropZone.addEventListener("dragenter", function () {
    dropZone.classList.add("active");
});

dropZone.addEventListener("dragleave", function () {
    dropZone.classList.remove("active");
});

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    dropZone.classList.remove("active");
    dropZone.appendChild(box);
});

این کار باعث می‌شود رابط کاربری برای کاربر قابل فهم‌تر باشد.

ساخت یک لیست قابل جابه‌جایی

حالا یک مثال کاربردی‌تر ایجاد کنیم.

فرض کنید چند وظیفه داریم:

<ul id="todo-list">
    <li draggable="true">طراحی صفحه اصلی</li>
    <li draggable="true">ساخت فرم ثبت‌نام</li>
    <li draggable="true">اتصال API</li>
    <li draggable="true">تست پروژه</li>
</ul>

هر li قابل Drag شدن است.

اما برای جابه‌جایی واقعی باید مشخص کنیم عنصر از کجا آمده و مقصد کجاست.

یک روش ساده این است که ID هر عنصر را منتقل کنیم.

مثال:

<ul id="todo-list">
    <li id="task1" draggable="true">طراحی صفحه اصلی</li>
    <li id="task2" draggable="true">ساخت فرم ثبت‌نام</li>
    <li id="task3" draggable="true">اتصال API</li>
    <li id="task4" draggable="true">تست پروژه</li>
</ul>

<ul id="done-list">
    <li>کارهای انجام‌شده</li>
</ul>

JavaScript:

const tasks = document.querySelectorAll("#todo-list li");
const doneList = document.getElementById("done-list");

tasks.forEach(function (task) {
    task.addEventListener("dragstart", function (event) {
        event.dataTransfer.setData("text/plain", task.id);
        event.dataTransfer.effectAllowed = "move";
    });
});

doneList.addEventListener("dragover", function (event) {
    event.preventDefault();

    event.dataTransfer.dropEffect = "move";
});

doneList.addEventListener("drop", function (event) {
    event.preventDefault();

    const taskId = event.dataTransfer.getData("text/plain");

    const task = document.getElementById(taskId);

    doneList.appendChild(task);
});

حالا کاربر می‌تواند وظیفه‌ها را از لیست اول به لیست دوم منتقل کند.

ساخت یک برد ساده Kanban

یکی از کاربردهای بسیار معروف Drag & Drop، ساخت Kanban Board است.

فرض کنید سه وضعیت داریم:

انجام نشده
     ↓
در حال انجام
     ↓
انجام شده

HTML:

<div class="board">

    <div class="column">
        <h2>انجام نشده</h2>

        <div class="task" id="task1" draggable="true">
            طراحی صفحه اصلی
        </div>

        <div class="task" id="task2" draggable="true">
            ساخت فرم ثبت‌نام
        </div>
    </div>

    <div class="column">
        <h2>در حال انجام</h2>
    </div>

    <div class="column">
        <h2>انجام شده</h2>
    </div>

</div>

JavaScript:

const tasks = document.querySelectorAll(".task");
const columns = document.querySelectorAll(".column");

tasks.forEach(function (task) {

    task.addEventListener("dragstart", function (event) {
        event.dataTransfer.setData("text/plain", task.id);
        event.dataTransfer.effectAllowed = "move";
    });

});

columns.forEach(function (column) {

    column.addEventListener("dragover", function (event) {
        event.preventDefault();

        event.dataTransfer.dropEffect = "move";
    });

    column.addEventListener("drop", function (event) {
        event.preventDefault();

        const taskId = event.dataTransfer.getData("text/plain");

        const task = document.getElementById(taskId);

        column.appendChild(task);
    });

});

با همین مقدار کد می‌توانیم یک نمونه بسیار ساده از Kanban ایجاد کنیم.

در پروژه‌های واقعی می‌توان این سیستم را بسیار پیشرفته‌تر کرد.

مثلاً:

  • ذخیره وضعیت در دیتابیس

  • مرتب‌سازی دقیق وظایف

  • نمایش انیمیشن

  • محدود کردن نوع Drop

  • ذخیره ترتیب آیتم‌ها

  • ارسال تغییرات به API

drag image چیست؟

مرورگر هنگام Drag معمولاً یک تصویر یا نمای بصری از عنصر در حال کشیدن نشان می‌دهد.

در صورت نیاز می‌توانیم تصویر Drag را نیز با:

setDragImage()

تغییر دهیم.

مثلاً:

event.dataTransfer.setDragImage(image, 10, 10);

این موضوع برای رابط‌های کاربری حرفه‌ای کاربرد دارد، اما در پروژه‌های ابتدایی نیازی نیست فعلاً از آن استفاده کنیم.

Drag & Drop فایل‌ها

یکی دیگر از کاربردهای مهم Drag & Drop، دریافت فایل از سیستم کاربر است.

برای مثال:

فایل را اینجا بکشید و رها کنید

کاربر می‌تواند یک فایل را از File Explorer سیستم خود به صفحه وب بکشاند.

در رویداد drop می‌توانیم فایل‌ها را از:

event.dataTransfer.files

دریافت کنیم.

مثال ساده:

<div id="drop-zone">
    فایل را اینجا رها کنید
</div>

JavaScript:

const dropZone = document.getElementById("drop-zone");

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

dropZone.addEventListener("drop", function (event) {
    event.preventDefault();

    const files = event.dataTransfer.files;

    console.log("تعداد فایل‌ها:", files.length);

    for (const file of files) {
        console.log(file.name);
        console.log(file.size);
        console.log(file.type);
    }
});

مثلاً اگر کاربر سه فایل انتخاب کند، می‌توانیم اطلاعات آن‌ها را در JavaScript دریافت کنیم.

در یک سیستم واقعی، بعد از این مرحله می‌توان فایل را با API به سرور ارسال کرد.

Drag & Drop و موبایل

یک نکته مهم برای طراحی سایت‌های امروزی وجود دارد.

Drag & Drop بومی HTML بیشتر برای تعاملات سنتی Drag، مخصوصاً در محیط دسکتاپ و با ماوس، طراحی شده است. بنابراین نباید فرض کنیم هر رابط Drag & Drop که روی دسکتاپ ساخته‌ایم دقیقاً همان تجربه را روی تمام دستگاه‌های لمسی خواهد داشت.

برای رابط‌های موبایلی بهتر است همیشه یک روش جایگزین نیز در نظر بگیریم.

مثلاً به‌جای اینکه فقط امکان کشیدن وجود داشته باشد، می‌توانیم گزینه‌هایی مانند:

انتقال به...

یا:

تغییر وضعیت

نیز ارائه کنیم.

این موضوع مخصوصاً برای سیستم‌هایی مانند Kanban، مدیریت وظایف و فروشگاه‌ها اهمیت دارد.

یک پروژه کامل ساده

حالا یک نمونه کوچک اما کامل بسازیم.

HTML

<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">

    <title>Drag & Drop</title>

    <style>
        body {
            font-family: sans-serif;
            padding: 30px;
        }

        .container {
            display: flex;
            gap: 30px;
        }

        .tasks,
        .drop-zone {
            width: 300px;
            min-height: 300px;
            padding: 20px;
            border: 2px dashed #999;
        }

        .task {
            padding: 15px;
            margin-bottom: 10px;
            background: orange;
            cursor: grab;
        }

        .drop-zone.active {
            border-color: green;
            background: #e8f5e9;
        }
    </style>
</head>

<body>

    <h1>Drag & Drop</h1>

    <div class="container">

        <div class="tasks">

            <h2>وظایف</h2>

            <div
                class="task"
                id="task1"
                draggable="true">
                طراحی سایت
            </div>

            <div
                class="task"
                id="task2"
                draggable="true">
                ساخت فرم
            </div>

            <div
                class="task"
                id="task3"
                draggable="true">
                تست پروژه
            </div>

        </div>

        <div
            class="drop-zone"
            id="drop-zone">

            <h2>انجام شده</h2>

        </div>

    </div>

    <script>

        const tasks = document.querySelectorAll(".task");

        const dropZone =
            document.getElementById("drop-zone");

        tasks.forEach(function (task) {

            task.addEventListener(
                "dragstart",
                function (event) {

                    event.dataTransfer.setData(
                        "text/plain",
                        task.id
                    );

                    event.dataTransfer.effectAllowed =
                        "move";
                }
            );

            task.addEventListener(
                "dragend",
                function () {

                    dropZone.classList.remove("active");

                }
            );

        });

        dropZone.addEventListener(
            "dragenter",
            function () {

                dropZone.classList.add("active");

            }
        );

        dropZone.addEventListener(
            "dragleave",
            function () {

                dropZone.classList.remove("active");

            }
        );

        dropZone.addEventListener(
            "dragover",
            function (event) {

                event.preventDefault();

                event.dataTransfer.dropEffect =
                    "move";

            }
        );

        dropZone.addEventListener(
            "drop",
            function (event) {

                event.preventDefault();

                const taskId =
                    event.dataTransfer.getData(
                        "text/plain"
                    );

                const task =
                    document.getElementById(taskId);

                dropZone.appendChild(task);

                dropZone.classList.remove("active");

            }
        );

    </script>

</body>
</html>

این پروژه کوچک تقریباً تمام مفاهیم اصلی مقاله را کنار هم قرار می‌دهد:

draggable
    ↓
dragstart
    ↓
setData()
    ↓
dragover
    ↓
preventDefault()
    ↓
drop
    ↓
getData()
    ↓
پیدا کردن عنصر
    ↓
appendChild()

اشتباهات رایج در Drag & Drop

فراموش کردن draggable

اگر بنویسیم:

<div>
    آیتم
</div>

این عنصر لزوماً به‌عنوان یک عنصر سفارشی Drag شده عمل نمی‌کند.

برای مثال آموزشی ما بهتر است بنویسیم:

<div draggable="true">
    آیتم
</div>

فراموش کردن preventDefault()

یکی از رایج‌ترین اشتباهات:

dropZone.addEventListener("dragover", function () {

});

است.

باید معمولاً بنویسیم:

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

چون Drop Target باید dragover را لغو کند تا Drop امکان‌پذیر شود.

تفاوت کلید setData و getData

اگر بنویسیم:

event.dataTransfer.setData(
    "text/plain",
    "hello"
);

باید هنگام دریافت نیز همان نوع داده را بخوانیم:

event.dataTransfer.getData(
    "text/plain"
);

مثلاً این:

getData("text/html")

داده‌ای که با text/plain ذخیره شده را دریافت نمی‌کند.

تصور اینکه drop بدون Drop Target اجرا می‌شود

اگر dragover را به‌درستی مدیریت نکنیم، ممکن است:

drop

اصلاً اجرا نشود.

بنابراین این الگو را به خاطر بسپارید:

dropZone.addEventListener("dragover", function (event) {
    event.preventDefault();
});

سپس:

dropZone.addEventListener("drop", function (event) {

});

فراموش کردن پاک‌سازی وضعیت

اگر هنگام Drag کلاس CSS اضافه می‌کنیم، بهتر است در پایان عملیات آن را حذف کنیم.

مثلاً:

task.addEventListener("dragend", function () {
    task.classList.remove("dragging");
});

استفاده از Drag & Drop به‌عنوان تنها روش تعامل در موبایل

اگر سایت شما برای موبایل طراحی می‌شود، فقط به Drag & Drop وابسته نباشید.

یک روش جایگزین مانند دکمه یا منوی انتقال نیز در نظر بگیرید.

تمرین‌های عملی

تمرین 1 — یک مربع قابل جابه‌جایی

یک صفحه بسازید که شامل:

  • یک مربع

  • یک Drop Zone

باشد.

وقتی مربع روی Drop Zone رها شد، آن را داخل Drop Zone قرار دهید.

تمرین 2 — چند آیتم

سه عنصر ایجاد کنید:

آیتم 1
آیتم 2
آیتم 3

کاربر باید بتواند هر سه را داخل یک Drop Zone قرار دهد.

تمرین 3 — انتقال بین دو لیست

دو لیست ایجاد کنید:

لیست اول
────────
آیتم 1
آیتم 2
آیتم 3

و:

لیست دوم
────────

کاربر باید بتواند آیتم‌ها را بین دو لیست جابه‌جا کند.

تمرین 4 — سه وضعیت

سه ستون ایجاد کنید:

انجام نشده
در حال انجام
انجام شده

کاربر باید بتواند هر وظیفه را بین ستون‌ها جابه‌جا کند.

چالش این مقاله

یک Task Board کوچک طراحی کنید.

صفحه باید شامل سه ستون باشد:

┌─────────────┐
│ انجام نشده  │
├─────────────┤
│ وظیفه 1     │
│ وظیفه 2     │
└─────────────┘

┌─────────────┐
│ در حال انجام│
├─────────────┤
│ وظیفه 3     │
└─────────────┘

┌─────────────┐
│ انجام شده   │
├─────────────┤
│             │
└─────────────┘

قابلیت‌های مورد انتظار:

  • Drag کردن وظیفه

  • Drop کردن در ستون جدید

  • تغییر ظاهر ستون هنگام ورود Drag

  • استفاده از dataTransfer

  • استفاده از dragstart

  • استفاده از dragover

  • استفاده از drop

  • استفاده از dragend

در مرحله بعدی می‌توانید وضعیت وظیفه‌ها را در localStorage ذخیره کنید؛ مفهومی که در مقاله شماره ۱۸ یاد گرفتیم.

نکات مهمی که یاد گرفتیم

در این مقاله با مفاهیم زیر آشنا شدیم:

  • مفهوم Drag & Drop

  • ویژگی draggable

  • رویداد dragstart

  • رویداد drag

  • رویداد dragend

  • رویداد dragenter

  • رویداد dragleave

  • رویداد dragover

  • رویداد drop

  • Drop Target

  • شیء DataTransfer

  • setData()

  • getData()

  • effectAllowed

  • dropEffect

  • انتقال اطلاعات

  • جابه‌جایی عناصر DOM

  • Drag & Drop فایل‌ها

  • ساخت لیست قابل جابه‌جایی

  • ساخت Kanban ساده

  • اهمیت پشتیبانی مناسب از موبایل

جمع‌بندی

Drag & Drop یکی از قابلیت‌های تعاملی HTML5 است که به ما اجازه می‌دهد عناصر یا اطلاعات را از یک قسمت صفحه گرفته و در قسمت دیگری رها کنیم. برای پیاده‌سازی آن معمولاً با رویدادهایی مانند dragstart، dragover، drop و dragend کار می‌کنیم و برای انتقال اطلاعات نیز از DataTransfer استفاده می‌کنیم. یکی از مهم‌ترین نکات این API این است که برای ایجاد یک Drop Target معمولاً باید در رویداد dragover از preventDefault() استفاده کنیم.

در این مقاله همچنین دیدیم که Drag & Drop فقط برای جابه‌جایی ظاهری عناصر نیست و می‌تواند برای انتقال داده، فایل، وظایف، محصولات و سایر اطلاعات نیز استفاده شود.

تا اینجا بخش مهمی از قابلیت‌های HTML و HTML5 را یاد گرفته‌ایم. در مقاله بعدی وارد مجموعه‌ای از قابلیت‌های کاربردی‌تر می‌شویم و با APIهای کاربردی HTML5 آشنا خواهیم شد.

۲۰

نظرات

کد در دسترس نیست

هنوز نظری ثبت نشده است.