draggable: وقتی کاربر می‌تونه المان‌ها رو با ماوس جابه‌جا کنه

حتماً تجربه‌ی کشیدن و رهاکردن یه فایل رو از یه پوشه به پوشه‌ی دیگه، یا جابه‌جاکردن کارت‌ها تو یه تخته‌ی وظایف (مثل Trello) داشتی. این رفتار، که بهش Drag and Drop می‌گن، یه قابلیت بومی HTML5 هست که با ویژگی draggable و یه سری رویداد مخصوص جاوااسکریپت پیاده‌سازی می‌شه.

draggable چیه؟

<div draggable="true">این المان قابل کشیدن است</div>

ویژگی draggable وقتی روی مقدار true تنظیم بشه، به کاربر اجازه می‌ده اون عنصر رو با ماوس بگیره و بکشه (Drag)؛ اما به‌تنهایی و بدون کد جاوااسکریپت اضافه، این کشیدن هیچ اثر عملی‌ای نداره؛ کاربر می‌تونه المان رو بکشه، اما جایی «رها» نمی‌شه چون هیچ منطقی برای اون تعریف نشده.

نکته‌ی مهم: draggable فقط نصف ماجراست

برخلاف بعضی ویژگی‌های دیگه‌ای که تو این فصل دیدیم (مثل contenteditable که خودش رفتار کامل رو فراهم می‌کنه)، draggable فقط قابلیت «کشیدن» رو فعال می‌کنه؛ برای اینکه این کشیدن به یه نتیجه‌ی معنادار (مثل جابه‌جایی واقعی یه آیتم بین دو لیست) برسه، حتماً باید با یه سری رویداد جاوااسکریپت خاص ترکیب بشه.

رویدادهای اصلی Drag and Drop API

HTML5 چند رویداد مشخص برای مدیریت این فرایند تعریف کرده که معمولاً با جاوااسکریپت گوش داده می‌شن:

  • dragstart: وقتی کاربر شروع به کشیدن یه المان می‌کنه.
  • dragover: وقتی المانی که داره کشیده می‌شه، روی یه ناحیه‌ی مقصد قرار می‌گیره.
  • drop: وقتی کاربر المان رو روی مقصد رها می‌کنه.
  • dragend: وقتی عملیات کشیدن (چه موفق چه ناموفق) تموم می‌شه.

یه مثال ساده و کامل با جاوااسکریپت

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

<div id="dropZone" style="border: 2px dashed gray; padding: 20px;">
  اینجا رها کن
</div>

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

  dragItem.addEventListener("dragstart", (e) => {
    e.dataTransfer.setData("text", e.target.id);
  });

  dropZone.addEventListener("dragover", (e) => {
    e.preventDefault(); // بدون این خط، drop اصلاً اجرا نمی‌شه
  });

  dropZone.addEventListener("drop", (e) => {
    e.preventDefault();
    const draggedId = e.dataTransfer.getData("text");
    dropZone.appendChild(document.getElementById(draggedId));
  });
</script>

بیا این کد رو مرحله‌به‌مرحله ببینیم: اول یه div با draggable="true" و یه div دیگه به‌عنوان ناحیه‌ی مقصد (dropZone) داریم. وقتی کاربر شروع به کشیدن dragItem می‌کنه، رویداد dragstart اجرا می‌شه و شناسه‌ی اون المان رو تو یه شیء مخصوص به اسم dataTransfer ذخیره می‌کنه. وقتی المان روی dropZone قرار می‌گیره، رویداد dragover اجرا می‌شه (که حتماً باید preventDefault صداش بزنیم، وگرنه رویداد drop اصلاً فعال نمی‌شه). در نهایت، وقتی کاربر رها می‌کنه، رویداد drop اجرا می‌شه و همون المان کشیده‌شده رو واقعاً داخل dropZone منتقل می‌کنه.

جزئیات کامل جاوااسکریپت این بخش (مثل dataTransfer و رویدادها) خارج از محدوده‌ی این دوره‌ی HTML5 است و بیشتر مربوط به دوره‌های جاوااسکریپت می‌شه؛ همین‌جا هدفمون فقط آشنایی با نقش HTML و ویژگی draggable در این فرایند بود.

کدام عناصر به‌صورت پیش‌فرض draggable هستن؟

یه نکته‌ی جالب اینه که بعضی عناصر HTML، بدون نیاز به نوشتن draggable="true"، به‌صورت پیش‌فرض قابل‌کشیدن هستن؛ مثل تصاویر (img) و لینک‌ها (a). اگه بخوای این رفتار پیش‌فرض رو غیرفعال کنی، می‌تونی صریحاً draggable="false" بذاری:

<img src="photo.jpg" alt="عکس محصول" draggable="false">

کاربردهای رایج Drag and Drop

  • تخته‌های مدیریت وظایف (مثل Trello) که کارت‌ها بین ستون‌های مختلف (در حال انجام، تمام‌شده) جابه‌جا می‌شن
  • آپلود فایل با کشیدن فایل از دسکتاپ مستقیم داخل مرورگر
  • مرتب‌سازی مجدد آیتم‌های یه لیست (مثل تغییر ترتیب تصاویر یه گالری)
  • ساخت رابط‌های کاربری سفارشی مثل چیدمان قابل‌تنظیم ویجت‌ها

یه محدودیت مهم: تجربه‌ی ضعیف روی موبایل

Drag and Drop API استاندارد HTML5، در اصل برای تعامل با ماوس طراحی شده و پشتیبانی مستقیمش از صفحه‌های لمسی (موبایل و تبلت) محدود و ناهماهنگه. برای پروژه‌هایی که باید تجربه‌ی کشیدن-رهاکردن یکسانی هم روی دسکتاپ و هم روی موبایل داشته باشن، معمولاً از کتابخانه‌های جاوااسکریپت تخصصی (که فراتر از این استاندارد پایه عمل می‌کنن) استفاده می‌شه؛ این موضوع خارج از محدوده‌ی این دوره‌ی مقدماتیه.

محدودیت دسترسی‌پذیری Drag and Drop

یه نکته‌ی مهم دیگه اینه که Drag and Drop به‌طور طبیعی برای کاربرانی که فقط از صفحه‌کلید استفاده می‌کنن (بدون ماوس)، یا کاربران صفحه‌خوان، به‌سختی قابل‌استفاده‌ست؛ چون این رفتار کاملاً بر پایه‌ی حرکت ماوسه. برای دسترسی‌پذیری کامل، معمولاً باید یه راه جایگزین (مثل دکمه‌های «انتقال به بالا» یا «انتقال به پایین») هم کنار قابلیت Drag and Drop ارائه بشه، تا کاربرانی که نمی‌تونن از ماوس استفاده کنن هم بتونن همون کار رو انجام بدن.

چند اشتباه رایج

  • تصور اینکه فقط نوشتن draggable="true" کافیه و رفتار کامل Drag and Drop خودش اتفاق میفته، بدون نیاز به جاوااسکریپت.
  • فراموش‌کردن e.preventDefault() تو رویداد dragover، که باعث می‌شه رویداد drop اصلاً اجرا نشه.
  • تکیه‌ی کامل به Drag and Drop بدون ارائه‌ی هیچ راه جایگزینی برای کاربران صفحه‌کلید یا صفحه‌خوان.
  • انتظار تجربه‌ی یکسان روی موبایل، بدون در نظر گرفتن محدودیت‌های این API روی صفحه‌های لمسی.

بهترین روش‌ها

  • همیشه یادت باشه draggable فقط نقطه‌ی شروعه؛ منطق واقعی جابه‌جایی باید با جاوااسکریپت نوشته بشه.
  • برای دسترسی‌پذیری، همیشه یه راه جایگزین (مثل دکمه یا منوی کیبوردی) کنار Drag and Drop در نظر بگیر.
  • برای پروژه‌هایی که نیاز به پشتیبانی خوب از موبایل دارن، استفاده از کتابخانه‌های تخصصی جاوااسکریپت رو در نظر بگیر.

نکته‌ی سئو

ویژگی draggable و رفتار Drag and Drop تأثیر مستقیمی روی سئو ندارند، چون این قابلیت‌ها کاملاً مربوط به تعامل کاربر بعد از بارگذاری صفحه هستند و محتوایی که موتورهای جستجو ایندکس می‌کنند را تغییر نمی‌دهند.

نکته‌ی دسترسی‌پذیری

Drag and Drop مبتنی بر ماوس، برای کاربران صفحه‌کلید یا صفحه‌خوان به‌طور پیش‌فرض به‌سختی قابل‌استفاده است؛ ارائه‌ی راه جایگزین (مثل دکمه‌های جابه‌جایی) برای این کاربران، یک اصل مهم طراحی فراگیر محسوب می‌شود.

جمع‌بندی

ویژگی draggable قابلیت کشیدن یک عنصر با ماوس را فعال می‌کند، اما برای رسیدن به یک رفتار کامل و معنادار Drag and Drop، حتماً باید با رویدادهای جاوااسکریپتی مثل dragstart، dragover و drop ترکیب شود. این قابلیت محدودیت‌هایی در پشتیبانی از دستگاه‌های لمسی و دسترسی‌پذیری دارد که باید در طراحی نهایی در نظر گرفته شوند.