تشرح هذه المشاركة أساسيات السحب والإفلات.
إنشاء محتوى قابل للسحب
في معظم المتصفّحات، تكون مقاطع النص والصور والروابط قابلة للسحب تلقائيًا. على سبيل المثال، إذا سحبت رابطًا على صفحة ويب، سيظهر لك مربّع صغير يحتوي على عنوان وعنوان URL، ويمكنك إفلاتهما على شريط العناوين أو على سطح المكتب لإنشاء اختصار أو الانتقال إلى الرابط. لجعل أنواع أخرى من المحتوى قابلة للسحب، عليك استخدام واجهات برمجة تطبيقات HTML5 Drag and Drop.
لجعل كائن قابلاً للسحب، اضبط 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%، على الرغم من أنك لم تحدد سلوك الانخفاض بعد.
إضافة إشارات مرئية إضافية
لمساعدة المستخدم في فهم كيفية التفاعل مع واجهتك، استخدِم معالِجات الأحداث
dragenter
وdragover
وdragleave
. في هذا المثال، تكون الأعمدة هي
أهداف طرحية بالإضافة إلى قابلة للسحب. يمكنك مساعدة المستخدم في فهم ذلك من خلال جعل الحدود متقطّعة عندما يُمسك بعنصر يتم سحبه فوق عمود. على سبيل المثال، يمكنك في خدمة مقارنة الأسعار (CSS) إنشاء فئة over
للعناصر التي تمثّل أهداف انخفاض:
.box.over {
border: 3px dotted #666;
}
بعد ذلك، اضبط معالِجات الأحداث في JavaScript وأضِف الفئة over
عند سحب العمود واحذفها عند مغادرة العنصر الذي تم سحبه. في
معالج dragend
، نحرص أيضًا على إزالة الفئات في نهاية عملية
السحب.
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
، عليك منع السلوك التلقائي للمتصفّح في عمليات إسقاط البيانات، والتي عادةً ما تكون نوعًا من عمليات إعادة التوجيه المزعجة. ولإجراء ذلك، يمكنك الاتصال بالرقم 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
وتتم قراءتها أو معالجتها في حدث الإفلات. يتيح لك الإجراء
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
. بدلاً من استخدام
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.
}
}
يمكنك العثور على مزيد من المعلومات حول ذلك في مقالة السحب والإفلات المخصّص.
مزيد من الموارد
- مواصفات السحب والإفلات
- واجهة برمجة التطبيقات HTML Drag and Drop API من MDN
- كيفية إنشاء أداة تحميل ملفات باستخدام لغة JavaScript العادية من خلال السحب والإفلات
- إنشاء لعبة ركن سيارات باستخدام واجهة برمجة التطبيقات HTML Drag and Drop API
- كيفية استخدام واجهة برمجة التطبيقات HTML Drag-and-Drop API في React