กำลังเลิกบล็อกการเข้าถึงคลิปบอร์ด

เข้าถึงคลิปบอร์ดสำหรับข้อความและรูปภาพได้อย่างปลอดภัยและไม่ถูกบล็อก

วิธีดั้งเดิมในการเข้าถึงคลิปบอร์ดของระบบคือผ่าน document.execCommand() สำหรับการโต้ตอบกับคลิปบอร์ด แม้ว่าวิธีตัดและวางนี้จะได้รับการรองรับอย่างกว้างขวาง แต่ก็มีข้อจำกัดคือ การเข้าถึงคลิปบอร์ดเป็นแบบซิงโครนัส และอ่านและเขียนได้เฉพาะใน DOM

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

Async Clipboard API แก้ไขปัญหาเหล่านี้ด้วยการมอบโมเดลสิทธิ์ที่กำหนดไว้อย่างดีซึ่งไม่ บล็อกหน้าเว็บ Async Clipboard API จำกัดการจัดการข้อความและรูปภาพ ในเบราว์เซอร์ส่วนใหญ่ แต่การรองรับจะแตกต่างกันไป โปรดศึกษาภาพรวมความเข้ากันได้ของเบราว์เซอร์อย่างละเอียดสำหรับแต่ละส่วนต่อไปนี้

คัดลอก: เขียนข้อมูลไปยังคลิปบอร์ด

writeText()

หากต้องการคัดลอกข้อความไปยังคลิปบอร์ด ให้เรียกใช้ writeText() เนื่องจาก API นี้เป็นแบบ อะซิงโครนัส ฟังก์ชัน writeText() จะแสดงผล Promise ที่แก้ไขหรือ ปฏิเสธโดยขึ้นอยู่กับว่าข้อความที่ส่งผ่านคัดลอกสำเร็จหรือไม่

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

Browser Support

  • Chrome: 66.
  • Edge: 79.
  • Firefox: 63.
  • Safari: 13.1.

Source

write()

จริงๆ แล้ว writeText() เป็นเพียงวิธีการที่สะดวกสำหรับเมธอด write() ทั่วไป ซึ่งยังช่วยให้คุณคัดลอกรูปภาพไปยังคลิปบอร์ดได้ด้วย เช่นเดียวกับ writeText() ซึ่งเป็นแบบอะซิงโครนัสและแสดงผล Promise

หากต้องการเขียนรูปภาพไปยังคลิปบอร์ด คุณต้องมีรูปภาพเป็น blob วิธีหนึ่งในการทำเช่นนี้คือการขอรูปภาพจากเซิร์ฟเวอร์โดยใช้ fetch() จากนั้นเรียกใช้ blob() ในการตอบกลับ

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

จากนั้นส่งอาร์เรย์ของออบเจ็กต์ ClipboardItem เป็นพารามิเตอร์ไปยังเมธอด write() ปัจจุบันคุณส่งรูปภาพได้ครั้งละ 1 รูปเท่านั้น แต่เราหวังว่าจะเพิ่ม การรองรับรูปภาพหลายรูปในอนาคต ClipboardItem รับออบเจ็กต์ที่มี ประเภท MIME ของรูปภาพเป็นคีย์และ Blob เป็นค่า สำหรับออบเจ็กต์ Blob ที่ได้จาก fetch() หรือ canvas.toBlob() พร็อพเพอร์ตี้ blob.type จะมีประเภท MIME ที่ถูกต้องสำหรับรูปภาพโดยอัตโนมัติ

try {
  const imgURL = '/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      // The key is determined dynamically based on the blob's type.
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

หรือจะเขียน Promise ไปยังออบเจ็กต์ ClipboardItem ก็ได้ สำหรับรูปแบบนี้ คุณต้องทราบประเภท MIME ของข้อมูลล่วงหน้า

try {
  const imgURL = '/images/generic/file.png';
  await navigator.clipboard.write([
    new ClipboardItem({
      // Set the key beforehand and write a promise as the value.
      'image/png': fetch(imgURL).then(response => response.blob()),
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

Browser Support

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127.
  • Safari: 13.1.

Source

เหตุการณ์การคัดลอก

ในกรณีที่ผู้ใช้เริ่มการคัดลอกไปยังคลิปบอร์ด และไม่ได้เรียกใช้ preventDefault() เหตุการณ์ copy จะมีพร็อพเพอร์ตี้ clipboardData ที่มีรายการในรูปแบบที่ถูกต้องอยู่แล้ว หากต้องการใช้ตรรกะของคุณเอง คุณต้องเรียกใช้ preventDefault() เพื่อ ป้องกันลักษณะการทำงานเริ่มต้นเพื่อสนับสนุนการใช้งานของคุณเอง ในกรณีนี้ clipboardData จะว่างเปล่า พิจารณาหน้าเว็บที่มีข้อความและรูปภาพ และเมื่อผู้ใช้เลือกทั้งหมดและเริ่มคัดลอกไปยังคลิปบอร์ด โซลูชันที่กำหนดเองควรทิ้งข้อความและคัดลอกเฉพาะรูปภาพ คุณทำได้ตามที่แสดงในตัวอย่างโค้ดด้านล่าง สิ่งที่ตัวอย่างนี้ไม่ได้กล่าวถึงคือวิธีกลับไปใช้ API เวอร์ชันก่อนหน้าเมื่อไม่รองรับ Clipboard API

<!-- The image we want on the clipboard. -->
<img src="kitten.webp" alt="Cute kitten.">
<!-- Some text we're not interested in. -->
<p>Lorem ipsum</p>
document.addEventListener("copy", async (e) => {
  // Prevent the default behavior.
  e.preventDefault();
  try {
    // Prepare an array for the clipboard items.
    let clipboardItems = [];
    // Assume `blob` is the blob representation of `kitten.webp`.
    clipboardItems.push(
      new ClipboardItem({
        [blob.type]: blob,
      })
    );
    await navigator.clipboard.write(clipboardItems);
    console.log("Image copied, text ignored.");
  } catch (err) {
    console.error(err.name, err.message);
  }
});

สำหรับกิจกรรม copy ให้ทำดังนี้

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 22.
  • Safari: 3.

Source

สำหรับ ClipboardItem

Browser Support

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127.
  • Safari: 13.1.

Source

วาง: อ่านข้อมูลจากคลิปบอร์ด

readText()

หากต้องการอ่านข้อความจากคลิปบอร์ด ให้เรียกใช้ navigator.clipboard.readText() แล้วรอ ให้ Promise ที่ส่งคืนแก้ไข

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

Browser Support

  • Chrome: 66.
  • Edge: 79.
  • Firefox: 125.
  • Safari: 13.1.

Source

read()

เมธอด navigator.clipboard.read() ยังเป็นแบบไม่พร้อมกันและแสดงผล Promise ด้วย หากต้องการอ่านรูปภาพจากคลิปบอร์ด ให้รับรายการออบเจ็กต์ ClipboardItem แล้ววนซ้ำ

แต่ละ ClipboardItem สามารถเก็บเนื้อหาในประเภทต่างๆ ได้ ดังนั้นคุณจะต้อง วนซ้ำในรายการประเภท โดยใช้ลูป for...of อีกครั้ง สำหรับแต่ละประเภท ให้เรียกใช้เมธอด getType() โดยมีประเภทปัจจุบันเป็นอาร์กิวเมนต์เพื่อรับ Blob ที่เกี่ยวข้อง เช่นเดียวกับก่อนหน้านี้ โค้ดนี้ไม่ได้เชื่อมโยงกับรูปภาพ และจะ ใช้ได้กับไฟล์ประเภทอื่นๆ ในอนาคต

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
}

Browser Support

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127.
  • Safari: 13.1.

Source

การทำงานกับไฟล์ที่วาง

ผู้ใช้จะใช้แป้นพิมพ์ลัดของคลิปบอร์ดได้ เช่น ctrl+c และ ctrl+v Chromium จะแสดงไฟล์แบบอ่านอย่างเดียวในคลิปบอร์ดตามที่ระบุไว้ด้านล่าง การดำเนินการนี้จะทริกเกอร์เมื่อผู้ใช้กดแป้นลัดวางเริ่มต้นของระบบปฏิบัติการ หรือเมื่อผู้ใช้คลิกแก้ไข แล้วคลิกวางในแถบเมนูของเบราว์เซอร์ ไม่จำเป็นต้องใช้โค้ดเพิ่มเติม

document.addEventListener("paste", async e => {
  e.preventDefault();
  if (!e.clipboardData.files.length) {
    return;
  }
  const file = e.clipboardData.files[0];
  // Read the file's contents, assuming it's a text file.
  // There is no way to write back to it.
  console.log(await file.text());
});

Browser Support

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

Source

เหตุการณ์วาง

ดังที่ได้กล่าวไว้ก่อนหน้านี้ เรามีแผนที่จะเปิดตัวเหตุการณ์เพื่อทำงานร่วมกับ Clipboard API แต่ในตอนนี้คุณสามารถใช้เหตุการณ์ paste ที่มีอยู่ได้ ซึ่งทำงานได้ดีกับ เมธอดแบบอะซิงโครนัสใหม่สำหรับการอ่านข้อความในคลิปบอร์ด เช่นเดียวกับcopy event อย่าลืมเรียกใช้ preventDefault()

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
});

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 22.
  • Safari: 3.

Source

การจัดการประเภท MIME หลายประเภท

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

ตัวอย่างต่อไปนี้แสดงวิธีดำเนินการ ตัวอย่างนี้ใช้ fetch() เพื่อรับ ข้อมูลรูปภาพ แต่ข้อมูลอาจมาจาก <canvas> หรือ File System Access API ก็ได้

async function copy() {
  const image = await fetch('kitten.png').then(response