توضّح هذه المشاركة أساسيات السحب والإفلات.
إنشاء محتوى قابل للسحب
في معظم المتصفّحات، تكون مقاطع النص والصور والروابط قابلة للسحب تلقائيًا. على سبيل المثال، إذا سحبت رابطًا على صفحة ويب، سيظهر لك مربّع صغير يحتوي على عنوان ومقدّمة وعنوان 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 بشكل متكرّر. يؤدي ذلك إلى تحميل المتصفّح الكثير من عمليات المعالجة غير الضرورية، ما قد يؤثر في تجربة المستخدم. ننصحك بشدة بتقليل عمليات redraws، وإذا كنت بحاجة إلى استخدام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