โพสต์นี้จะอธิบายพื้นฐานของการลากและวาง
สร้างเนื้อหาที่ลากได้
ในเบราว์เซอร์ส่วนใหญ่ คุณสามารถลากการเลือกข้อความ รูปภาพ และลิงก์ได้โดยค่าเริ่มต้น เช่น หากลากลิงก์ในหน้าเว็บ คุณจะเห็นช่องเล็กๆ ที่มีชื่อและ URL ซึ่งคุณสามารถวางในแถบที่อยู่หรือเดสก์ท็อปเพื่อสร้างทางลัดหรือไปยังลิงก์ได้ หากต้องการให้เนื้อหาประเภทอื่นๆ ลากได้ คุณต้องใช้ HTML5 Drag and Drop API
หากต้องการให้วัตถุลากได้ ให้ตั้งค่า draggable=true
ในองค์ประกอบนั้น คุณสามารถเปิดใช้การลากได้เกือบทุกอย่าง ซึ่งรวมถึงรูปภาพ ไฟล์ ลิงก์ ไฟล์ หรือมาร์กอัปในหน้าเว็บ
ตัวอย่างต่อไปนี้เป็นการสร้างอินเทอร์เฟซเพื่อจัดเรียงคอลัมน์ที่วางไว้ด้วยตารางกริด CSS ใหม่ มาร์กอัปพื้นฐานสำหรับคอลัมน์จะมีลักษณะดังนี้ โดยตั้งค่าแอตทริบิวต์ 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);
});
});
โค้ดนี้มีข้อควรทราบ 2-3 ข้อดังนี้
การดำเนินการเริ่มต้นสำหรับเหตุการณ์
dragover
คือการตั้งค่าพร็อพเพอร์ตี้dataTransfer.dropEffect
เป็น"none"
พร็อพเพอร์ตี้dropEffect
จะกล่าวถึงในหน้านี้ต่อ สำหรับตอนนี้ ขอทราบไว้ว่า จะป้องกันไม่ให้เหตุการณ์drop
เริ่มทำงาน หากต้องการลบล้างลักษณะการทำงานนี้ ให้เรียกใช้e.preventDefault()
แนวทางปฏิบัติแนะนำอีกอย่างหนึ่งคือให้แสดงผลfalse
ในตัวแฮนเดิลเดียวกันนั้นตัวแฮนเดิลเหตุการณ์
dragenter
ใช้เพื่อสลับคลาสover
แทนdragover
หากคุณใช้dragover
เหตุการณ์จะเริ่มทำงานซ้ำๆ ขณะที่ผู้ใช้เก็บรายการที่ลากไปไว้เหนือคอลัมน์ ซึ่งทำให้คลาส CSS สลับไปมาซ้ำๆ ซึ่งทำให้เบราว์เซอร์ต้องทำงานแสดงผลที่ไม่จำเป็นเป็นจำนวนมาก ซึ่งอาจส่งผลต่อประสบการณ์ของผู้ใช้ เราขอแนะนำอย่างยิ่งให้ลดการรีมาร์เก็ตติ้งให้น้อยที่สุด และหากคุณต้องการใช้dragover
ให้พิจารณาการควบคุมหรือลดระดับ Listener เหตุการณ์
เปิดตัวแอปให้เสร็จสมบูรณ์
หากต้องการประมวลผลการเปิดตัว ให้เพิ่ม Listener เหตุการณ์สำหรับเหตุการณ์ 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)
หมายความว่าคุณสามารถตั้งค่าไอคอนลากแทนการใช้ความคิดเห็น "รูปภาพผี" เริ่มต้นของเบราว์เซอร์
อัปโหลดไฟล์
ตัวอย่างง่ายๆ นี้ใช้คอลัมน์เป็นทั้งแหล่งที่มาการลากและเป้าหมายการลาก ซึ่งอาจเกิดขึ้นใน UI ที่ขอให้ผู้ใช้จัดเรียงรายการใหม่ ในบางสถานการณ์ เป้าหมายการลากและแหล่งที่มาอาจเป็นองค์ประกอบประเภทอื่น เช่น ในอินเทอร์เฟซที่ผู้ใช้ต้องเลือกรูปภาพ 1 รูปเป็นรูปภาพหลักสำหรับผลิตภัณฑ์โดยการลากรูปภาพที่เลือกไปไว้ในเป้าหมาย
การลากและวางมักใช้เพื่อให้ผู้ใช้ลากรายการจากเดสก์ท็อปไปยังแอปพลิเคชัน ความแตกต่างหลักอยู่ที่ตัวแฮนเดิล 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.
}
}
ดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งนี้ได้ในการลากและวางที่กําหนดเอง