Bỏ chặn quyền truy cập vào bảng nhớ tạm

Truy cập vào bảng nhớ tạm an toàn hơn và bỏ chặn đối với văn bản và hình ảnh

Cách truyền thống để truy cập vào bảng nhớ tạm của hệ thống là thông qua document.execCommand() cho các tương tác với bảng nhớ tạm. Mặc dù được hỗ trợ rộng rãi, phương pháp cắt giảm và việc dán sẽ phải trả phí: quyền truy cập bảng nhớ tạm đồng bộ và chỉ có thể đọc và ghi vào DOM.

Điều này là bình thường với một đoạn văn bản nhỏ, nhưng có nhiều trường hợp chặn để chuyển bảng nhớ tạm là một trải nghiệm không tốt. Tốn thời gian dọn dẹp vệ sinh hoặc có thể cần giải mã hình ảnh trước khi có thể dán nội dung một cách an toàn. Trình duyệt có thể cần tải hoặc tài nguyên được liên kết cùng dòng từ một tài liệu đã dán. Như vậy chặn trang trong khi chờ trên đĩa hoặc mạng. Hãy tưởng tượng rằng bạn có thể thêm quyền vào danh sách kết hợp, đòi hỏi trình duyệt phải chặn trang trong khi yêu cầu truy cập bảng nhớ tạm. Đồng thời, các quyền được áp dụng document.execCommand() cho hoạt động tương tác với bảng nhớ tạm được xác định không rõ ràng và thay đổi giữa các trình duyệt.

Chiến lược phát hành đĩa đơn API Bảng nhớ tạm không đồng bộ giải quyết những vấn đề này, cung cấp một mô hình quản lý quyền được xác định rõ ràng mà không chặn trang. API Bảng nhớ tạm không đồng bộ chỉ được dùng để xử lý văn bản và hình ảnh trên hầu hết các trình duyệt, nhưng việc hỗ trợ sẽ khác nhau. Hãy nhớ nghiên cứu kỹ trình duyệt tổng quan về khả năng tương thích cho từng phần sau.

Sao chép: ghi dữ liệu vào bảng nhớ tạm

writeText()

Để sao chép văn bản vào bảng nhớ tạm, hãy gọi writeText(). Vì API này không đồng bộ, hàm writeText() sẽ trả về một Promise (Lời hứa) giúp phân giải hoặc từ chối tuỳ thuộc vào việc văn bản đã chuyển có được sao chép thành công hay không:

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);
  }
}

Hỗ trợ trình duyệt

  • 66
  • 79
  • 63
  • 13,1

Nguồn

write()

Thực ra, writeText() chỉ là một phương thức tiện lợi cho write() chung Phương thức này cũng cho phép bạn sao chép hình ảnh vào bảng nhớ tạm. Giống như writeText(), không đồng bộ và trả về một Promise.

Để ghi hình ảnh vào bảng nhớ tạm, bạn cần hình ảnh làm blob. Chỉ có một cách thực hiện thao tác này bằng cách yêu cầu hình ảnh từ một máy chủ bằng fetch(), sau đó gọi blob() trên của bạn.

Yêu cầu hình ảnh từ máy chủ có thể không như mong muốn hoặc không khả thi đối với có nhiều lý do. May mắn là bạn cũng có thể vẽ hình ảnh vào canvas và gọi đến canvas toBlob() .

Tiếp theo, hãy truyền một mảng các đối tượng ClipboardItem dưới dạng tham số đến write() . Hiện tại, bạn chỉ có thể chuyển một hình ảnh mỗi lần, nhưng chúng tôi hy vọng sẽ thêm hỗ trợ nhiều hình ảnh trong tương lai. ClipboardItem lấy một đối tượng có loại MIME của hình ảnh là khoá và blob làm giá trị. Đối với blob các đối tượng thu được từ fetch() hoặc canvas.toBlob(), thuộc tính blob.type tự động chứa loại MIME chính xác cho một hình ảnh.

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);
}

Ngoài ra, bạn có thể viết lời hứa cho đối tượng ClipboardItem. Đối với mẫu này, bạn cần biết trước loại MIME của dữ liệu.

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);
}

Hỗ trợ trình duyệt

  • 66
  • 79
  • 127
  • 13,1

Nguồn

Sự kiện sao chép

Trong trường hợp người dùng bắt đầu sao chép bảng nhớ tạm và không gọi preventDefault(), copy sự kiện bao gồm thuộc tính clipboardData với các mục đã ở định dạng phù hợp. Nếu muốn triển khai logic của riêng mình, bạn cần gọi preventDefault() để ngăn hành vi mặc định có lợi cho cách triển khai của riêng bạn. Trong trường hợp này, clipboardData sẽ trống. Xem xét trang có văn bản và hình ảnh, khi người dùng chọn tất cả và bắt đầu một bản sao trên bảng nhớ tạm, giải pháp tuỳ chỉnh của bạn sẽ loại bỏ văn bản và chỉ sao chép hình ảnh đó. Bạn có thể thực hiện điều này như trong mã mẫu bên dưới. Nội dung không được đề cập trong ví dụ này là cách quay lại phần trước Các API khi API Bảng nhớ tạm không được hỗ trợ.

<!-- 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);
  }
});

Đối với sự kiện copy:

Hỗ trợ trình duyệt

  • 1
  • 12
  • 22
  • 3

Nguồn

Đối với ClipboardItem:

Hỗ trợ trình duyệt

  • 76
  • 79
  • 127
  • 13,1

Nguồn

Dán: đọc dữ liệu từ bảng nhớ tạm

readText()

Để đọc văn bản trên bảng nhớ tạm, hãy gọi navigator.clipboard.readText() rồi đợi đối với lời hứa được trả về sẽ giải quyết:

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);
  }
}

Hỗ trợ trình duyệt

  • 66
  • 79
  • 125
  • 13,1

Nguồn

read()

Phương thức navigator.clipboard.read() cũng không đồng bộ và trả về một hứa hẹn. Để đọc hình ảnh từ bảng nhớ tạm, hãy lấy danh sách ClipboardItem đối tượng, sau đó lặp lại các đối tượng đó.

Mỗi ClipboardItem có thể chứa nội dung theo nhiều kiểu, do đó bạn cần lặp lại danh sách các loại, một lần nữa bằng cách sử dụng vòng lặp for...of. Đối với mỗi loại, gọi phương thức getType() có loại hiện tại làm đối số để lấy giá trị blob tương ứng. Như trước đây, mã này không gắn liền với hình ảnh và sẽ làm việc với các loại tệp khác trong tương lai.

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);
  }
}

Hỗ trợ trình duyệt

  • 66
  • 79
  • 127
  • 13,1

Nguồn

Làm việc với các tệp đã dán

Việc này rất hữu ích khi người dùng có thể sử dụng các phím tắt trong bảng nhớ tạm, chẳng hạn như ctrl cctrl v. Chromium hiển thị các tệp chỉ có thể đọc trên bảng nhớ tạm như được nêu dưới đây. Lệnh này kích hoạt khi người dùng nhấn vào phím tắt dán mặc định của hệ điều hành hoặc khi người dùng nhấp vào Chỉnh sửa rồi nhấp vào Dán trong thanh trình đơn của trình duyệt. Bạn không cần thêm mã sửa ống nước nữa.

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());
});

Hỗ trợ trình duyệt

  • 3
  • 12
  • 3.6
  • 4

Nguồn

Sự kiện dán

Như đã lưu ý trước đó, chúng tôi có kế hoạch giới thiệu các sự kiện hoạt động với API Bảng nhớ tạm, nhưng hiện tại, bạn có thể sử dụng sự kiện paste hiện có. Công cụ này hoạt động tốt với không đồng bộ để đọc văn bản trong bảng nhớ tạm. Tương tự như với sự kiện copy, đừng quên gọi điện cho preventDefault().

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

Hỗ trợ trình duyệt

  • 1
  • 12
  • 22
  • 3

Nguồn

Xử lý nhiều loại MIME

Hầu hết các phương pháp triển khai đều đặt nhiều định dạng dữ liệu vào bảng nhớ tạm cho một lần triển khai hoặc sao chép. Có hai lý do dẫn đến trường hợp này: với tư cách là nhà phát triển ứng dụng, bạn có không có cách nào để biết khả năng của ứng dụng mà người dùng muốn sao chép văn bản hoặc hình ảnh vào, và nhiều ứng dụng hỗ trợ dán dữ liệu có cấu trúc dưới dạng văn bản thuần tuý. Thông thường, hiển thị cho người dùng mục trong trình đơn Chỉnh sửa có tên chẳng hạn như Dán và kiểu khớp hoặc Dán mà không định dạng.

Ví dụ sau đây trình bày cách thực hiện việc này. Ví dụ này sử dụng fetch() để lấy dữ liệu hình ảnh, nhưng cũng có thể bắt nguồn từ một <canvas> hoặc API Truy cập hệ thống tệp.

async function copy() {
  const image = await fetch('kitten.png').then(response => response.blob());
  const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
  const item = new ClipboardItem({
    'text/plain': text,
    'image/png': image
  });
  await navigator.clipboard.write([item]);
}

Tính bảo mật và quyền truy cập

Quyền truy cập vào bảng nhớ tạm luôn khiến các trình duyệt lo ngại về bảo mật. Không có một trang có thể tự động sao chép mọi loại nội dung độc hại vào bảng nhớ tạm của người dùng và sẽ tạo ra kết quả thảm khốc khi được dán. Hãy tưởng tượng một trang web tự động sao chép rm -rf / hoặc hình ảnh về bom giải nén vào bảng nhớ tạm.

Lời nhắc của trình duyệt yêu cầu người dùng cấp quyền sử dụng bảng nhớ tạm.
Lời nhắc cấp quyền cho API Bảng nhớ tạm.

Việc cung cấp cho các trang web quyền đọc mà không bị chặn quyền truy cập vào bảng nhớ tạm thậm chí còn hữu ích hơn thực sự khó khăn. Người dùng thường xuyên sao chép thông tin nhạy cảm như mật khẩu và thông tin cá nhân vào bảng nhớ tạm để bất kỳ trang nào sau đó có thể đọc mà không cần kiến thức của người dùng.

Giống như nhiều API mới, API Bảng nhớ tạm chỉ được hỗ trợ cho các trang được phân phát qua HTTPS. Để góp phần ngăn chặn hành vi sai trái, chỉ cho phép truy cập vào bảng nhớ tạm khi một trang thẻ hoạt động. Các trang trong thẻ đang hoạt động có thể ghi vào bảng nhớ tạm mà không cần yêu cầu quyền, nhưng việc đọc từ bảng nhớ tạm luôn đòi hỏi quyền.

Các quyền sao chép và dán đã được thêm vào API Quyền. Quyền clipboard-write tự động được cấp cho các trang khi chúng được thẻ hoạt động. Bạn phải yêu cầu quyền clipboard-read. Bạn có thể thực hiện việc này bằng cách cố gắng đọc dữ liệu từ bảng nhớ tạm. Mã bên dưới cho biết tên thứ hai:

const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);

// Listen for changes to the permission state
permissionStatus.onchange = () => {
  console.log(permissionStatus.state);
};

Bạn cũng có thể kiểm soát việc có cần cử chỉ của người dùng để gọi thao tác cắt hay không dán bằng cách sử dụng tuỳ chọn allowWithoutGesture. Giá trị mặc định cho giá trị này sẽ khác nhau tuỳ theo trình duyệt. Vì vậy, bạn nên luôn bao gồm thông tin này.

Dưới đây là trường hợp tính chất không đồng bộ của API Bảng nhớ tạm thực sự hữu ích: cố gắng đọc hoặc ghi dữ liệu bảng nhớ tạm tự động nhắc người dùng nếu chưa được cấp. Vì API này dựa trên hứa hẹn, điều này là hoàn toàn minh bạch và người dùng từ chối cấp quyền vào bảng nhớ tạm lời hứa từ chối để trang có thể phản hồi thích hợp.

Vì trình duyệt chỉ cho phép truy cập vào bảng nhớ tạm khi trang là thẻ đang hoạt động, bạn sẽ thấy rằng một số ví dụ ở đây không chạy nếu được dán trực tiếp vào bảng điều khiển của trình duyệt, vì chính công cụ cho nhà phát triển là thẻ hoạt động. Có một mẹo: hãy trì hoãn truy cập bảng nhớ tạm bằng setTimeout(), sau đó nhấp nhanh vào bên trong trang để đặt tiêu điểm trước khi gọi các hàm:

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

Tích hợp chính sách về quyền

Để sử dụng API trong iframe, bạn cần bật API bằng Chính sách về quyền, xác định một cơ chế cho phép bật và chọn tắt nhiều tính năng và API của trình duyệt. Cụ thể, bạn cần truyền hoặc cả clipboard-read hay clipboard-write, tuỳ thuộc vào nhu cầu của ứng dụng.

<iframe
    src="index.html"
    allow="clipboard-read; clipboard-write"
>
</iframe>

Phát hiện tính năng

Để dùng API Bảng nhớ tạm không đồng bộ mà vẫn hỗ trợ tất cả trình duyệt, hãy kiểm thử navigator.clipboard rồi quay lại các phương thức trước đó. Ví dụ: dưới đây là cách bạn có thể triển khai việc dán để bao gồm các trình duyệt khác.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  let text;
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText();
  }
  else {
    text = e.clipboardData.getData('text/plain');
  }
  console.log('Got pasted text: ', text);
});

Đó chưa phải là tất cả. Trước API Bảng nhớ tạm không đồng bộ, có sự pha trộn giữa những cách triển khai khác nhau cho tính năng sao chép và dán trên các trình duyệt web. Trong hầu hết các trình duyệt, Tính năng sao chép và dán của chính trình duyệt có thể được kích hoạt bằng cách sử dụng document.execCommand('copy')document.execCommand('paste'). Nếu văn bản Nếu được sao chép, là một chuỗi không có trong DOM, chuỗi này phải được đưa vào DOM và đã chọn:

button.addEventListener('click', (e) => {
  const input = document.createElement('input');
  input.style.display = 'none';
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  const result = document.execCommand('copy');
  if (result === 'unsuccessful') {
    console.error('Failed to copy text.');
  }
  input.remove();
});

Bản thu thử

Bạn có thể chơi bằng API Bảng nhớ tạm không đồng bộ trong bản minh hoạ dưới đây. Bắt chước có thể phối lại bản minh hoạ văn bản hoặc bản minh hoạ hình ảnh để thử nghiệm với chúng.

Ví dụ đầu tiên minh hoạ việc di chuyển văn bản vào và ra khỏi bảng nhớ tạm.

Để thử dùng API với hình ảnh, hãy dùng bản minh hoạ này. Lưu ý rằng chỉ hỗ trợ PNG và chỉ trong một số trình duyệt.

Xác nhận

API Bảng nhớ tạm không đồng bộ do Darwin triển khai HuangGary Kačmarčík. Darwin cũng cung cấp bản minh hoạ. Cảm ơn Kyarik và một lần nữa Gary Kačmarčík vì xem các phần của bài viết này.

Hình ảnh chính của Markus Winkler đang bật Không hiển thị màn hình.