โพสต์นี้อธิบายพื้นฐานของการลากและวาง
สร้างเนื้อหาแบบลากได้
ในเบราว์เซอร์ส่วนใหญ่ คุณจะลากการเลือกข้อความ รูปภาพ และลิงก์ได้โดยค่าเริ่มต้น เช่น หากลากลิงก์ในหน้าเว็บ คุณจะเห็นช่องเล็กๆ ที่แสดงชื่อและ URL ซึ่งคุณสามารถวางในแถบที่อยู่หรือเดสก์ท็อปเพื่อสร้างทางลัดหรือไปยังลิงก์ได้ หากต้องการให้ลากเนื้อหาประเภทอื่นๆ ได้ คุณต้องใช้ API การลากและวางของ HTML5
หากต้องการทำให้วัตถุลากได้ ให้ตั้งค่า 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;
}
คุณสามารถดูผลลัพธ์ได้ในการสาธิตต่อไปนี้ หากต้องการให้ทำเช่นนี้ คุณต้องมีเบราว์เซอร์จากเดสก์ท็อป อุปกรณ์เคลื่อนที่ไม่รองรับการลากและวาง ลากและวางคอลัมน์ 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.files
แทนที่จะใช้ dataTransfer.getData()
เพื่อเข้าถึงไฟล์ ดังนี้
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.
}
}
ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในการลากและวางที่กำหนดเอง