Dalam tutorial ini, kita akan melihat mekanisme drag-and-drop bawaan di halaman.
Demi keadilan, perlu dicatat bahwa mekanisme ini dapat diimplementasikan dengan menggunakan mouse event, seperti yang Ilya Kantor tunjukkan dalam buku teksnya , namun, kami akan menggunakan alat asli berdasarkan spesifikasinya .
Dukungan teknologi:
Pratinjau:
Tugas kita adalah sebagai berikut: untuk mengimplementasikan daftar tugas, yang terdiri dari tiga kolom: semua tugas, tugas yang sedang berjalan, tugas yang sudah selesai. Tentu saja, aplikasi harus menyediakan kemampuan untuk menambah dan menghapus tugas. Selain itu, kemungkinan pengaturan tugas yang sewenang-wenang harus disediakan. Ini adalah salah satu bagian yang lebih menarik dari tutorial - melacak item di bawah item yang diseret dan menentukan di mana item yang diseret harus ditempatkan di atas atau di bawah item yang dilacak. Bootstrap
akan digunakan untuk penataan . Jika Anda tertarik, silakan ikuti saya.
Markup:
<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>
Di sini kita memiliki wadah dengan bidang untuk memasukkan teks tugas dan tombol untuk menambahkannya ke daftar (kelompok masukan), serta tiga kolom wadah (kelompok daftar) untuk semua tugas (daftar-todos), tugas yang sedang berlangsung (dalam -progress-list) dan tugas yang diselesaikan (complete-list). Sedangkan untuk atribut "data", mereka dimaksudkan untuk memisahkan gaya dan pengelolaan: kelas untuk gaya, data untuk pengelolaan.
Gaya:
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;
}
Kelas "dalam proses" dan "selesai" berfungsi sebagai indikator bahwa tugas ada di kolom yang sesuai. Kelas "drop" dirancang untuk memvisualisasikan tugas mengenai zona drop.
Sebelum beralih ke skrip, perhatikan bahwa kami tidak akan menggunakan semua peristiwa tarik, tetapi sebagian besar yang utama.
Kami mendefinisikan wadah utama di mana pencarian elemen akan dilakukan dan di mana pemrosesan acara akan didelegasikan:
const main = document.querySelector("main");
Kami menerapkan menambahkan dan menghapus tugas melalui pemrosesan klik:
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();
}
}
});
Ayo langsung menyeret.
Untuk memulainya, mari implementasikan masuk ke zona "melempar" dan meninggalkannya dengan menambahkan / menghapus kelas yang sesuai:
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");
}
});
Selanjutnya, kami memproses awal seret:
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);
}
});
Sekarang kita perlu melacak elemen di bawah elemen yang diseret. Ini diperlukan untuk mengatur tugas dalam daftar secara sewenang-wenang, mis. menukar tugas di kolom di tempat. Saat menangani event "mousemove", metode "elementFromPoint (x, y)" digunakan untuk ini. Keindahan antarmuka ini adalah untuk menentukan elemen "yang mendasari", kita hanya perlu menangani peristiwa "dragover":
// ""
let elemBelow = "";
main.addEventListener("dragover", (e) => {
// ;
//
e.preventDefault();
// ;
//
elemBelow = e.target;
});
Terakhir, kami menangani acara "drop":
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";
}
}
});
Itu saja. Seperti yang Anda lihat, tidak ada yang rumit. Tapi apa kemungkinan untuk menambahkan interaktivitas ke halaman. Tetap menunggu sampai browser seluler menerapkan teknologi ini, dan semua orang akan senang.
Saya harap Anda menemukan sesuatu yang menarik untuk diri Anda sendiri. Terima kasih atas perhatiannya dan semoga harimu menyenangkan.