ทลายอุปสรรคด้วย DataTransfer API

อนุญาตให้ผู้ใช้แชร์ข้อมูลนอกหน้าต่างเบราว์เซอร์

คุณอาจเคยได้ยินเกี่ยวกับ DataTransfer API ซึ่งเป็นส่วนหนึ่งของ HTML5 Drag and Drop API และเหตุการณ์ในคลิปบอร์ด ซึ่งสามารถใช้เพื่อโอนข้อมูลระหว่างแหล่งที่มาและปลายทางที่รับได้

การรองรับเบราว์เซอร์

  • Chrome: 3.
  • Edge: 12.
  • Firefox: 3.5
  • Safari: 4.

แหล่งที่มา

การโต้ตอบแบบลากและวางและคัดลอกและวางมักใช้สำหรับการโต้ตอบภายในหน้าเว็บเพื่อโอนข้อความธรรมดาจากจุด A ไปยังจุด B แต่สิ่งที่มักถูกมองข้ามคือความสามารถในการใช้การโต้ตอบแบบเดียวกันนี้เพื่อขยายขอบเขตการทำงานนอกหน้าต่างเบราว์เซอร์

ทั้งการลากและวางในตัวของเบราว์เซอร์และการโต้ตอบด้วยการคัดลอกและวางสามารถสื่อสารกับแอปพลิเคชันอื่นๆ ไม่ว่าจะเป็นเว็บหรือแอปพลิเคชันอื่นๆ และไม่ได้เชื่อมโยงกับต้นทางใดๆ API รองรับรายการข้อมูลหลายรายการที่มีลักษณะการทํางานแตกต่างกันไปตามปลายทางที่โอนข้อมูลไป เว็บแอปพลิเคชันสามารถส่งและรับข้อมูลที่โอนเมื่อฟังเหตุการณ์ขาเข้า

ความสามารถนี้อาจเปลี่ยนวิธีที่เราคิดเกี่ยวกับการแชร์และการทํางานร่วมกันในเว็บแอปพลิเคชันบนเดสก์ท็อป การโอนข้อมูลระหว่างแอปพลิเคชันไม่จำเป็นต้องอาศัยการผสานรวมที่เชื่อมโยงกันอย่างแน่นหนาอีกต่อไป แต่ให้ผู้ใช้ควบคุมการโอนข้อมูลไปยังที่ใดก็ได้อย่างเต็มที่แทน

ตัวอย่างการโต้ตอบที่เป็นไปได้ด้วย DataTransfer API (วิดีโอไม่มีเสียง)

การโอนข้อมูล

คุณจะต้องลากและวางหรือคัดลอกและวางเพื่อเริ่มต้นใช้งาน ตัวอย่างด้านล่างแสดงการโต้ตอบแบบลากและวาง แต่กระบวนการคัดลอกและวางจะคล้ายกัน หากไม่คุ้นเคยกับ Drag and Drop API เรามีบทความที่ยอดเยี่ยมซึ่งอธิบายการลากและวาง HTML5 ไว้ให้

การให้ข้อมูลที่มีคีย์เป็นประเภท MIME จะช่วยให้คุณโต้ตอบกับแอปพลิเคชันภายนอกได้อย่างอิสระ ตัวแก้ไข WYSIWYG, เครื่องมือแก้ไขข้อความ และเบราว์เซอร์ส่วนใหญ่จะตอบสนองต่อประเภท mime "พื้นฐาน" ที่ใช้ในตัวอย่างที่ด้านล่าง

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', 'Foo bar');
  event.dataTransfer.setData('text/html', '<h1>Foo bar</h1>');
  event.dataTransfer.setData('text/uri-list', 'https://example.com');
});

โปรดสังเกตพร็อพเพอร์ตี้ event.dataTransfer การดำเนินการนี้จะแสดงผลอินสแตนซ์ของ DataTransfer ดังที่จะเห็น บางครั้งพร็อพเพอร์ตี้ที่มีชื่ออื่นจะแสดงผลออบเจ็กต์นี้

การโอนข้อมูลที่ได้รับจะทํางานเกือบเหมือนกับการโอนข้อมูล ฟังเหตุการณ์การรับ (drop หรือ paste) และอ่านคีย์ เมื่อลากเหนือองค์ประกอบหนึ่งๆ เบราว์เซอร์จะมีสิทธิ์เข้าถึงเฉพาะคีย์ type ของข้อมูลเท่านั้น คุณจะเข้าถึงข้อมูลได้หลังจากการปล่อยข้อมูลเท่านั้น

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  console.log(event.dataTransfer.types);
  // Without this, the drop event won't fire.
  event.preventDefault();
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  // Log all the transferred data items to the console.
  for (let type of event.dataTransfer.types) {
    console.log({ type, data: event.dataTransfer.getData(type) });
  }
  event.preventDefault();
});

ประเภท MIME 3 ประเภทที่รองรับอย่างกว้างขวางในแอปพลิเคชันต่างๆ ได้แก่

  • text/html: แสดงผลเพย์โหลด HTML ในองค์ประกอบ contentEditable และเครื่องมือแก้ไขข้อความแบบริชมีเดีย (WYSIWYG) เช่น Google เอกสาร, Microsoft Word และอื่นๆ
  • text/plain: กําหนดค่าขององค์ประกอบอินพุต เนื้อหาของเครื่องมือแก้ไขโค้ด และค่าสำรองจาก text/html
  • text/uri-list: นำทางไปยัง URL เมื่อวางในแถบ URL หรือหน้าเบราว์เซอร์ ระบบจะสร้างทางลัด URL เมื่อวางในไดเรกทอรีหรือเดสก์ท็อป

text/html มีประโยชน์อย่างมากเนื่องจากมีการใช้กันอย่างแพร่หลายในโปรแกรมแก้ไข WYSIWYG คุณสามารถฝังทรัพยากรได้โดยใช้ URL ข้อมูลหรือ URL ที่เข้าถึงได้แบบสาธารณะ เช่นเดียวกับในเอกสาร HTML ซึ่งวิธีนี้ใช้ได้ดีกับการส่งออกภาพ (เช่น จากผืนผ้าใบ) ไปยังเครื่องมือแก้ไข เช่น Google เอกสาร

const redPixel = 'data:image/gif;base64,R0lGODdhAQABAPAAAP8AAAAAACwAAAAAAQABAAACAkQBADs=';
const html = '<img src="' + redPixel + '" width="100" height="100" alt="" />';
event.dataTransfer.setData('text/html', html);

โอนโดยใช้การคัดลอกและวาง

การใช้ DataTransfer API กับการโต้ตอบการคัดลอกและวางแสดงอยู่ด้านล่าง โปรดทราบว่าพร็อพเพอร์ตี้ชื่อ clipboardData จะแสดงผลออบเจ็กต์ DataTransfer สําหรับเหตุการณ์คลิปบอร์ด

// Listen to copy-paste events on the document.
document.addEventListener('copy', (event) => {
  const copySource = document.querySelector('#copySource');
  // Only copy when the `activeElement` (i.e., focused element) is,
  // or is within, the `copySource` element.
  if (copySource.contains(document.activeElement)) {
    event.clipboardData.setData('text/plain', 'Foo bar');
    event.preventDefault();
  }
});

document.addEventListener('paste', (event) => {
  const pasteTarget = document.querySelector('#pasteTarget');
  if (pasteTarget.contains(document.activeElement)) {
    const data = event.clipboardData.getData('text/plain');
    console.log(data);
  }
});

รูปแบบข้อมูลที่กําหนดเอง

คุณใช้ประเภท MIME พื้นฐานได้ แต่จะระบุข้อมูลที่มีการโอนด้วยคีย์ใดก็ได้ ซึ่งอาจเป็นประโยชน์สำหรับการโต้ตอบข้ามเบราว์เซอร์ภายในแอปพลิเคชัน ดังที่แสดงด้านล่าง คุณสามารถโอนข้อมูลที่ซับซ้อนมากขึ้นได้โดยใช้ฟังก์ชัน JSON.stringify() และ JSON.parse()

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  const data = { foo: 'bar' };
  event.dataTransfer.setData('my-custom-type', JSON.stringify(data));
});

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  // Only allow dropping when our custom data is available.
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
  }
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
    const dataString = event.dataTransfer.getData('my-custom-type');
    const data = JSON.parse(dataString);
    console.log(data);
  }
});

การเชื่อมต่อเว็บ

แม้ว่ารูปแบบที่กำหนดเองจะเหมาะสําหรับการสื่อสารระหว่างแอปพลิเคชันที่คุณควบคุมได้ แต่ก็จํากัดผู้ใช้เมื่อโอนข้อมูลไปยังแอปพลิเคชันที่ไม่ได้ใช้รูปแบบของคุณ หากต้องการเชื่อมต่อกับแอปพลิเคชันของบุคคลที่สามในเว็บ คุณต้องใช้รูปแบบข้อมูลที่เป็นแบบสากล

มาตรฐาน JSON-LD (Linked Data) เหมาะสําหรับกรณีนี้อย่างยิ่ง ไฟล์นี้เบาและอ่านและเขียนใน JavaScript ได้ง่าย Schema.org มีประเภทที่กําหนดไว้ล่วงหน้าจํานวนมากที่นําไปใช้ได้ และยังมีคําจํากัดความสคีมาที่กำหนดเองด้วย

const data = {
  '@context': 'https://schema.org',
  '@type': 'ImageObject',
  contentLocation: 'Venice, Italy',
  contentUrl: 'venice.jpg',
  datePublished: '2010-08-08',
  description: 'I took this picture during our honey moon.',
  name: 'Canal in Venice',
};
event.dataTransfer.setData('application/ld+json', JSON.stringify(data));

เมื่อใช้ประเภท Schema.org คุณสามารถเริ่มต้นด้วยประเภท Thing ทั่วไป หรือใช้ประเภทที่ใกล้เคียงกับ Use Case ของคุณ เช่น Event, Person, MediaObject, Place หรือแม้แต่ประเภทที่เฉพาะเจาะจงสูง เช่น MedicalEntity หากจำเป็น เมื่อใช้ TypeScript คุณจะใช้คำจำกัดความอินเทอร์เฟซจากคำจำกัดความประเภท schema-dts ได้

การส่งและรับข้อมูล JSON-LD จะช่วยให้เว็บเชื่อมต่อกันและเปิดกว้างมากขึ้น เมื่อใช้แอปพลิเคชันที่พูดภาษาเดียวกัน คุณสามารถสร้างการผสานรวมที่ลึกซึ้งกับแอปพลิเคชันภายนอกได้ คุณไม่จำเป็นต้องผสานรวม API ที่ยุ่งยาก เนื่องจากข้อมูลทั้งหมดที่จำเป็นจะรวมอยู่ในข้อมูลที่โอน

ลองนึกถึงโอกาสทั้งหมดในการโอนข้อมูลระหว่างแอปพลิเคชัน (เว็บ) ใดก็ได้โดยไม่มีข้อจำกัด เช่น การแชร์กิจกรรมจากปฏิทินไปยังแอป ToDo ที่คุณชื่นชอบ แนบไฟล์เสมือนจริงไปกับอีเมล การแชร์รายชื่อติดต่อ เยี่ยมไปเลยใช่ไหม การเปลี่ยนแปลงนี้เริ่มต้นจากคุณ 🙌

ข้อกังวล

แม้ว่า DataTransfer API จะพร้อมใช้งานแล้วในปัจจุบัน แต่ก็มีสิ่งที่ควรทราบก่อนการผสานรวม

ความเข้ากันได้กับเบราว์เซอร์

เบราว์เซอร์บนเดสก์ท็อปทั้งหมดรองรับเทคนิคที่อธิบายข้างต้น แต่อุปกรณ์เคลื่อนที่ไม่รองรับ เราได้ทดสอบเทคนิคนี้ในเบราว์เซอร์หลักๆ ทั้งหมด (Chrome, Edge, Firefox, Safari) และระบบปฏิบัติการ (Android, ChromeOS, iOS, macOS, Ubuntu Linux และ Windows) แต่น่าเสียดายที่ Android และ iOS ไม่ผ่านการทดสอบ แม้ว่าเบราว์เซอร์จะพัฒนาไปอย่างต่อเนื่อง แต่ปัจจุบันเทคนิคนี้ใช้ได้กับเบราว์เซอร์เดสก์ท็อปเท่านั้น

การค้นพบได้

การลากและวาง รวมถึงการคัดลอกและวางเป็นการโต้ตอบระดับระบบเมื่อทำงานบนคอมพิวเตอร์เดสก์ท็อป ซึ่งย้อนไปถึง GUI แรกเมื่อกว่า 40 ปีที่แล้ว ลองนึกถึงจำนวนครั้งที่คุณใช้การโต้ตอบเหล่านี้ในการจัดระเบียบไฟล์ ซึ่งยังไม่พบบ่อยนักในเว็บ

คุณจะต้องแจ้งให้ผู้ใช้ทราบเกี่ยวกับการโต้ตอบแบบใหม่นี้ และสร้างรูปแบบ UX เพื่อให้ผู้ใช้จดจำได้ โดยเฉพาะอย่างยิ่งสำหรับผู้ที่เคยใช้คอมพิวเตอร์แต่ใช้อุปกรณ์เคลื่อนที่เป็นหลัก

การช่วยเหลือพิเศษ

การลากและวางไม่ใช่การโต้ตอบที่เข้าถึงได้ง่าย แต่ DataTransfer API สามารถใช้ร่วมกับการคัดลอกและวางได้ด้วย ตรวจสอบว่าคุณได้ฟังเหตุการณ์การคัดลอกและวาง การเพิ่มแอตทริบิวต์นี้ไม่ยากเย็นอะไร และผู้ใช้จะขอบคุณคุณที่เพิ่มแอตทริบิวต์

ความปลอดภัยและความเป็นส่วนตัว

คุณควรคำนึงถึงความปลอดภัยและความเป็นส่วนตัวบางอย่างเมื่อใช้เทคนิคนี้

  • แอปพลิเคชันอื่นๆ ในอุปกรณ์ของผู้ใช้จะเข้าถึงข้อมูลคลิปบอร์ดได้
  • เว็บแอปพลิเคชันที่คุณลากจะมีสิทธิ์เข้าถึงคีย์ประเภท ไม่ใช่ข้อมูล ข้อมูลจะพร้อมใช้งานเมื่อวางหรือวางเท่านั้น
  • ข้อมูลที่ได้รับควรได้รับการปฏิบัติเช่นเดียวกับอินพุตอื่นๆ ของผู้ใช้ โดยต้องกรองและตรวจสอบก่อนใช้งาน

เริ่มต้นใช้งานไลบรารีตัวช่วยของ Transmat

คุณสนใจที่จะใช้ DataTransfer API ในแอปพลิเคชันหรือไม่ ลองดูไลบรารี Transmat ใน GitHub ไลบรารีโอเพนซอร์สนี้ปรับความแตกต่างของเบราว์เซอร์ ให้ยูทิลิตี JSON-LD มีเครื่องมือตรวจสอบเพื่อตอบสนองต่อเหตุการณ์การโอนเพื่อไฮไลต์พื้นที่วาง และให้คุณผสานรวมการดำเนินการโอนข้อมูลในการใช้งานการลากและวางที่มีอยู่

import { Transmat, TransmatObserver, addListeners } from 'transmat';

// Send data on drag/copy.
addListeners(myElement, 'transmit', (event) => {
  const transmat = new Transmat(event);
  transmat.setData({
    'text/plain': 'Foobar',
    'application/json': { foo: 'bar' },
  });
});

// Receive data on drop/paste.
addListeners(myElement, 'receive', (event) => {
  const transmat = new Transmat(event);
  if (transmat.hasType('application/json') && transmat.accept()) {
    const data = JSON.parse(transmat.getData('application/json'));
  }
});

// Observe transfer events and highlight drop areas.
const obs = new TransmatObserver((entries) => {
  for (const entry of entries) {
    const transmat = new Transmat(entry.event);
    if (transmat.hasMimeType('application/json')) {
      entry.target.classList.toggle('drag-over', entry.isTarget);
      entry.target.classList.toggle('drag-active', entry.isActive);
    }
  }
});
obs.observe(myElement);

ขอขอบคุณ

รูปภาพหลักโดย Luba Ertel จาก Unsplash