في هذا البرنامج التعليمي ، سنلقي نظرة على آلية السحب والإفلات المضمنة في الصفحة.
من أجل الإنصاف ، تجدر الإشارة إلى أنه يمكن تنفيذ هذه الآلية باستخدام أحداث الماوس ، كما يوضح إيليا كانتور في كتابه المدرسي ، ومع ذلك ، سنستخدم أدوات أصلية بناءً على المواصفات .
دعم التكنولوجيا:
معاينة:
مهمتنا هي كما يلي: تنفيذ قائمة مهام تتكون من ثلاثة أعمدة: جميع المهام ، المهام في طور التنفيذ ، المهام المكتملة. بالطبع يجب أن يوفر التطبيق إمكانية إضافة وحذف المهام. بالإضافة إلى ذلك ، ينبغي توفير إمكانية الترتيب التعسفي للمهام. هذا هو أحد الأجزاء الأكثر إثارة للاهتمام في البرنامج التعليمي - تتبع العنصر الموجود أسفل العنصر المسحوب وتحديد مكان العنصر المسحوب أعلى أو أسفل العنصر المتعقب.
سيتم استخدام Bootstrap للتصميم .
إذا كنت مهتمًا ، من فضلك اتبعني.
وضع علامة على:
<head>
<!-- Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
crossorigin="anonymous"
/>
<!-- custom CSS -->
<link rel="stylesheet" href="style.css" />
</head>
<body class="container">
<h1>Drag & Drop Example</h1>
<main class="row">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Enter new todo: </span>
</div>
<input
type="text"
class="form-control"
placeholder="todo4"
data-name="todo-input"
/>
<div class="input-group-append">
<button class="btn btn-success" data-name="add-btn">Add</button>
</div>
</div>
<div class="col-4">
<h3>Todos</h3>
<ul class="list-group" data-name="todos-list">
<li class="list-group-item" data-id="1" draggable="true">
<p>todo1</p>
<button
class="btn btn-outline-danger btn-sm"
data-name="remove-btn"
>
X
</button>
</li>
<li class="list-group-item" data-id="2" draggable="true">
<p>todo2</p>
<button
class="btn btn-outline-danger btn-sm"
data-name="remove-btn"
>
X
</button>
</li>
<li class="list-group-item" data-id="3" draggable="true">
<p>todo3</p>
<button
class="btn btn-outline-danger btn-sm"
data-name="remove-btn"
>
X
</button>
</li>
</ul>
</div>
<div class="col-4">
<h3>In Progress</h3>
<ul class="list-group" data-name="in-progress-list"></ul>
</div>
<div class="col-4">
<h3>Completed</h3>
<ul class="list-group" data-name="completed-list"></ul>
</div>
</main>
<!-- custom JS -->
<script src="script.js"></script>
</body>
لدينا هنا حاوية بها حقل لإدخال نص مهمة وزر لإضافتها إلى القائمة (مجموعة الإدخال) ، بالإضافة إلى ثلاثة أعمدة حاوية (قائمة - مجموعة) لجميع المهام (قائمة المهام) ، المهام قيد التقدم (في -قائمة التقدم) والمهام المنجزة (القائمة المنجزة). أما بالنسبة إلى سمات "البيانات" ، فهي تهدف إلى فصل التصميم والتحكم: الفئات - للأنماط ، والبيانات - للإدارة.
الأنماط:
body {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #222;
}
main {
max-width: 600px;
}
.input-group {
margin: 1rem;
}
.list-group {
min-height: 100px;
height: 100%;
}
.list-group-item {
display: flex;
justify-content: space-between;
align-items: center;
}
div + div {
border-right: 1px dotted #222;
}
h3 {
text-align: center;
}
p {
margin: 0;
}
.completed p {
text-decoration: line-through;
}
.in-progress p {
border-bottom: 1px dashed #222;
}
.drop {
background: linear-gradient(#eee, transparent);
border-radius: 4px;
}
تعمل الفصول "قيد التقدم" و "المكتملة" كمؤشرات على أن المهمة موجودة في العمود المقابل. تم تصميم فئة "الإسقاط" لتصور المهمة التي تصل إلى منطقة الإسقاط.
قبل الانتقال إلى البرنامج النصي ، لاحظ أننا لن نستخدم جميع أحداث السحب والإفلات ، ولكن معظم الأحداث الرئيسية.
نحدد الحاوية الرئيسية التي سيتم فيها البحث عن العناصر والتي سيتم تفويض معالجة الحدث إليها:
const main = document.querySelector("main");
نقوم بتنفيذ إضافة وحذف المهام من خلال نقرة:
main.addEventListener("click", (e) => {
//
if (e.target.tagName === "BUTTON") {
// "data-name"
const { name } = e.target.dataset;
//
if (name === "add-btn") {
//
const todoInput = main.querySelector('[data-name="todo-input"]');
//
if (todoInput.value.trim() !== "") {
//
const value = todoInput.value;
//
const template = `
<li class="list-group-item" draggable="true" data-id="${Date.now()}">
<p>${value}</p>
<button class="btn btn-outline-danger btn-sm" data-name="remove-btn">X</button>
</li>
`;
//
const todosList = main.querySelector('[data-name="todos-list"]');
//
todosList.insertAdjacentHTML("beforeend", template);
//
todoInput.value = "";
}
//
} else if (name === "remove-btn") {
//
e.target.parentElement.remove();
}
}
});
دعنا نذهب مباشرة إلى السحب.
بادئ ذي بدء ، دعنا ننفذ الدخول إلى منطقة "الرمي" وتركها بإضافة / إزالة الفئة المناسبة:
main.addEventListener("dragenter", (e) => {
//
if (e.target.classList.contains("list-group")) {
e.target.classList.add("drop");
}
});
main.addEventListener("dragleave", (e) => {
if (e.target.classList.contains("drop")) {
e.target.classList.remove("drop");
}
});
بعد ذلك ، نقوم بمعالجة بداية السحب:
main.addEventListener("dragstart", (e) => {
//
if (e.target.classList.contains("list-group-item")) {
// "dataTransfer" ;
// dataTransfer HTML - text/html,
//
e.dataTransfer.setData("text/plain", e.target.dataset.id);
}
});
نحتاج الآن إلى تتبع العنصر بطريقة ما تحت العنصر المسحوب. هذا ضروري من أجل الترتيب التعسفي للمهام في القائمة ، أي مبادلة المهام في عمود في الأماكن. عند معالجة حدث "mousemove" ، يتم استخدام طريقة "elementFromPoint (x، y)" لهذا الغرض. يكمن جمال هذه الواجهة في أنه لتحديد العنصر "الأساسي" ، نحتاج فقط إلى معالجة حدث "السحب":
// ""
let elemBelow = "";
main.addEventListener("dragover", (e) => {
// ;
//
e.preventDefault();
// ;
//
elemBelow = e.target;
});
أخيرًا ، نتعامل مع حدث "الإسقاط":
main.addEventListener("drop", (e) => {
// , dataTransfer
const todo = main.querySelector(
`[data-id="${e.dataTransfer.getData("text/plain")}"]`
);
// , -
if (elemBelow === todo) {
return;
}
// , ,
if (elemBelow.tagName === "P" || elemBelow.tagName === "BUTTON") {
elemBelow = elemBelow.parentElement;
}
// ,
if (elemBelow.classList.contains("list-group-item")) {
// , :
// ;
//
// ( )
//
const center =
elemBelow.getBoundingClientRect().y +
elemBelow.getBoundingClientRect().height / 2;
//
// ,
// ,
if (e.clientY > center) {
if (elemBelow.nextElementSibling !== null) {
elemBelow = elemBelow.nextElementSibling;
} else {
return;
}
}
elemBelow.parentElement.insertBefore(todo, elemBelow);
//
// ,
todo.className = elemBelow.className;
}
//
if (e.target.classList.contains("list-group")) {
//
// ""
e.target.append(todo);
// ""
if (e.target.classList.contains("drop")) {
e.target.classList.remove("drop");
}
// ,
const { name } = e.target.dataset;
if (name === "completed-list") {
if (todo.classList.contains("in-progress")) {
todo.classList.remove("in-progress");
}
todo.classList.add("completed");
} else if (name === "in-progress-list") {
if (todo.classList.contains("completed")) {
todo.classList.remove("completed");
}
todo.classList.add("in-progress");
} else {
todo.className = "list-group-item";
}
}
});
هذا كل شئ. كما ترون ، لا شيء معقد. ولكن ما هي احتمالات إضافة التفاعلية إلى الصفحة. يبقى الانتظار حتى تنفذ متصفحات الجوال هذه التقنية ، وسيكون الجميع سعداء.
أتمنى أن تجد شيئًا مثيرًا للاهتمام لنفسك. شكرا لك على اهتمامك واستمتع بيومك.