API การลากและวางของ HTML5

โพสต์นี้จะอธิบายพื้นฐานของการลากและวาง

สร้างเนื้อหาที่ลากได้

ในเบราว์เซอร์ส่วนใหญ่ คุณสามารถลากการเลือกข้อความ รูปภาพ และลิงก์ได้โดยค่าเริ่มต้น ตัวอย่างเช่น หากคุณลากลิงก์ลงในหน้าเว็บ คุณจะเห็นช่องเล็กๆ ที่มีไอคอน ชื่อและ 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 ประการคือ

  • การดำเนินการเริ่มต้น สําหรับเหตุการณ์ 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;
}

คุณดูผลลัพธ์ได้ในการสาธิตต่อไปนี้ คุณต้องมี เบราว์เซอร์ในเดสก์ท็อป ระบบไม่รองรับ 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.
  }
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ใน การลากและวางที่กำหนดเอง

แหล่งข้อมูลเพิ่มเติม