HTML5 Drag and Drop API

این پست اصول اولیه کشیدن و رها کردن را توضیح می دهد.

محتوای قابل کشیدن ایجاد کنید

در اکثر مرورگرها، انتخاب متن، تصاویر و پیوندها به طور پیش فرض قابل کشیدن هستند. به عنوان مثال، اگر پیوندی را روی یک صفحه وب بکشید، کادر کوچکی با عنوان و URL مشاهده خواهید کرد که می توانید آن را در نوار آدرس یا دسکتاپ رها کنید تا میانبر ایجاد کنید یا به پیوند بروید. برای اینکه انواع دیگر محتوا قابل کشیدن باشد، باید از HTML5 Drag and Drop API استفاده کنید.

برای اینکه یک شی قابل کشیدن باشد، draggable=true را روی آن عنصر تنظیم کنید. تقریباً هر چیزی را می توان با کشیدن فعال کرد، از جمله تصاویر، فایل ها، پیوندها، فایل ها یا هر نشانه گذاری در صفحه شما.

مثال زیر یک رابط برای تنظیم مجدد ستون هایی که با CSS Grid چیده شده اند ایجاد می کند. نشانه گذاری اصلی برای ستون ها به شکل زیر است، با ویژگی draggable برای هر ستون به true :

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

در اینجا CSS برای عناصر کانتینر و جعبه است. تنها CSS مربوط به ویژگی کشیدن، ویژگی مکان cursor: move است. بقیه کد، چیدمان و استایل عناصر ظرف و جعبه را کنترل می کند.

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

در این مرحله می توانید آیتم ها را بکشید، اما هیچ اتفاق دیگری نمی افتد. برای افزودن رفتار، باید از JavaScript API استفاده کنید.

برای کشیدن رویدادها گوش دهید

برای نظارت بر فرآیند کشیدن، می توانید به هر یک از رویدادهای زیر گوش دهید:

برای مدیریت جریان کشیدن، به نوعی عنصر منبع (جایی که کشیدن شروع می شود)، بار داده (چیزی که کشیده می شود) و یک هدف (منطقه ای برای گرفتن افت) نیاز دارید. عنصر منبع می تواند تقریباً هر نوع عنصری باشد. هدف، منطقه دراپ یا مجموعه ای از مناطق دراپ است که داده هایی را که کاربر در تلاش است رها کند، می پذیرد. همه عناصر نمی توانند هدف باشند. به عنوان مثال، هدف شما نمی تواند یک تصویر باشد.

شروع و پایان یک دنباله کشیدن

پس از اینکه ویژگی‌های draggable="true" را روی محتوای خود تعریف کردید، یک کنترل کننده رویداد dragstart را برای شروع دنباله کشیدن برای هر ستون ضمیمه کنید.

این کد زمانی که کاربر شروع به کشیدن ستون می کند کدورت آن را روی 40 درصد قرار می دهد، سپس با پایان یافتن رویداد کشیدن، آن را به 100 درصد برمی گرداند.

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

نتیجه را می توان در دمو Glitch زیر مشاهده کرد. یک مورد را بکشید و کدورت آن تغییر می کند. از آنجایی که عنصر منبع دارای رویداد dragstart است، تنظیم this.style.opacity روی 40% به کاربر بازخورد بصری می دهد که آن عنصر انتخاب فعلی در حال جابجایی است. هنگامی که آیتم را رها می کنید، عنصر منبع به کدورت 100% باز می گردد، حتی اگر هنوز رفتار drop را تعریف نکرده باشید.

نشانه های بصری اضافی را اضافه کنید

برای کمک به کاربر در درک نحوه تعامل با رابط خود، از گرداننده رویداد dragenter ، dragover و dragleave استفاده کنید. در این مثال، ستون‌ها علاوه بر قابل کشیدن، هدف‌های افت هستند. هنگامی که یک مورد کشیده شده را روی یک ستون نگه می دارد، به کاربر کمک کنید تا این موضوع را درک کند. به عنوان مثال، در CSS خود، ممکن است یک over class برای عناصری ایجاد کنید که اهداف drop هستند:

.box.over {
  border: 3px dotted #666;
}

سپس، در جاوا اسکریپت، کنترل‌کننده‌های رویداد را راه‌اندازی کنید، زمانی که ستون روی آن کشیده می‌شود، کلاس over را اضافه کنید، و زمانی که عنصر کشیده شده خارج شد، آن را حذف کنید. در dragend handler ما همچنین مطمئن می‌شویم که کلاس‌های انتهای درگ را حذف می‌کنیم.

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

در این کد چند نکته قابل توجه است:

  • اقدام پیش‌فرض برای رویداد dragover تنظیم ویژگی dataTransfer.dropEffect روی "none" است. ویژگی dropEffect بعداً در این صفحه پوشش داده می شود. در حال حاضر، فقط بدانید که از شلیک رویداد drop جلوگیری می کند. برای لغو این رفتار، e.preventDefault() را فراخوانی کنید. روش خوب دیگر این است که false در همان کنترل کننده بازگردانید.

  • کنترل کننده رویداد dragenter برای جابجایی کلاس over به جای dragover استفاده می شود. اگر از dragover استفاده می کنید، در حالی که کاربر مورد کشیده شده را روی یک ستون نگه می دارد، رویداد به طور مکرر فعال می شود و باعث می شود کلاس CSS به طور مکرر جابجا شود. این باعث می شود مرورگر کارهای رندر غیر ضروری زیادی را انجام دهد که می تواند بر تجربه کاربر تأثیر بگذارد. ما اکیداً توصیه می‌کنیم که بازنویسی‌ها را به حداقل برسانید، و اگر نیاز به استفاده از dragover دارید، شنونده رویداد خود را کاهش دهید یا حذف کنید .

قطره را کامل کنید

برای پردازش افت، یک شنونده رویداد برای رویداد drop اضافه کنید. در drop handler، باید از رفتار پیش‌فرض مرورگر برای سقوط جلوگیری کنید، که معمولاً نوعی تغییر مسیر آزاردهنده است. برای انجام این کار، e.stopPropagation() را فراخوانی کنید.

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

حتماً کنترل کننده جدید را در کنار سایر کنترل کننده ها ثبت کنید:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

اگر کد را در این مرحله اجرا کنید، مورد به مکان جدید نمی افتد. برای انجام این کار، از شی DataTransfer استفاده کنید.

ویژگی dataTransfer داده های ارسال شده را در یک عمل کشیدن نگه می دارد. dataTransfer در رویداد dragstart تنظیم می‌شود و در رویداد drop خوانده یا پردازش می‌شود. فراخوانی e.dataTransfer.setData(mimeType, dataPayload) به شما امکان می دهد نوع MIME شی و بار داده را تنظیم کنید.

در این مثال، به کاربران اجازه می‌دهیم ترتیب ستون‌ها را دوباره مرتب کنند. برای انجام این کار، ابتدا باید HTML عنصر منبع را هنگام شروع کشیدن ذخیره کنید:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

در رویداد drop ، افت ستون را با تنظیم HTML ستون منبع به HTML ستون هدفی که داده ها را روی آن رها کرده اید، پردازش می کنید. این شامل بررسی این است که کاربر به همان ستونی که از آن کشیده شده برنگردد.

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

نتیجه را می توانید در دمو زیر مشاهده کنید. برای انجام این کار، به یک مرورگر دسکتاپ نیاز دارید. Drag and Drop API در تلفن همراه پشتیبانی نمی‌شود. ستون A را در بالای ستون B بکشید و رها کنید و به نحوه تغییر مکان آنها توجه کنید:

ویژگی های کشیدن بیشتر

شی dataTransfer ویژگی هایی را برای ارائه بازخورد بصری به کاربر در طول فرآیند کشیدن و کنترل نحوه پاسخ هر هدف قطره به یک نوع داده خاص را نشان می دهد.

  • dataTransfer.effectAllowed نوع "کشیدن" را که کاربر می تواند روی عنصر انجام دهد را محدود می کند. در مدل پردازش کشیدن و رها کردن برای مقداردهی اولیه dropEffect در طول رویدادهای dragenter و dragover استفاده می شود. این ویژگی می تواند مقادیر زیر را داشته باشد: none , copy , copyLink , copyMove , link , linkMove , move , all و uninitialized .
  • dataTransfer.dropEffect بازخوردی را که کاربر در طول رویدادهای dragenter و dragover دریافت می کند، کنترل می کند. هنگامی که کاربر نشانگر خود را روی یک عنصر هدف نگه می دارد، مکان نما مرورگر نشان می دهد که چه نوع عملیاتی قرار است انجام شود، مانند کپی یا حرکت. افکت می تواند یکی از مقادیر زیر را داشته باشد: none ، copy ، link ، move .
  • e.dataTransfer.setDragImage(imgElement, x, y) به این معنی است که به جای استفاده از بازخورد پیش‌فرض «تصویر شبح» مرورگر، می‌توانید یک نماد کشیدن تنظیم کنید.

آپلود فایل

این مثال ساده از یک ستون به عنوان منبع کشیدن و هدف کشیدن استفاده می کند. این ممکن است در رابط کاربری اتفاق بیفتد که از کاربر می‌خواهد موارد را مجدداً مرتب کند. در برخی شرایط، هدف کشیدن و منبع ممکن است انواع عناصر مختلف باشد، مانند یک رابط که در آن کاربر باید با کشیدن تصویر انتخاب شده روی یک هدف، یک تصویر را به عنوان تصویر اصلی یک محصول انتخاب کند.

کشیدن و رها کردن اغلب استفاده می شود تا به کاربران اجازه دهد موارد را از دسکتاپ خود به یک برنامه بکشند. تفاوت اصلی در drop handler شماست. به جای استفاده از dataTransfer.getData() برای دسترسی به فایل ها، داده های آنها در ویژگی dataTransfer.files قرار می گیرد:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

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

منابع بیشتر