クリップボード アクセスのブロックを解除する

テキストや画像に対するより安全でブロックされていないクリップボードへのアクセス

システムのクリップボードにアクセスする従来の方法は、 document.execCommand() 使用できます。この切断方法は広くサポートされていますが、 貼り付けには代償が伴い、クリップボードへのアクセスは同期的で、 DOM に書き込みます

テキストが小さい場合は問題ありませんが、 クリップボード転送用のページは使いにくい。時間のかかるサニタイズや コンテンツを安全に貼り付ける前に、画像のデコードが必要になる場合があります。ブラウザ 貼り付けたドキュメントからリンクされたリソースを読み込むか、インライン化する必要がある場合があります。この場合、 ディスクまたはネットワークで待機している間、ページをブロックします。権限の追加を想像してみてください ブラウザがページをブロックしなければならず、 クリップボードへのアクセスを制御します。それと同時に、Compute Engine の クリップボード操作の document.execCommand() が緩く定義されており、可変 おすすめします。

Async Clipboard API 適切に定義された権限モデルを提供することで、こうした問題に対処できます。 表示されます。Async Clipboard API での処理がテキストと画像に限定される ほとんどのブラウザで使用できますが、サポート状況はブラウザによって異なります。ブラウザをよく調査し、 互換性の概要について説明します。

コピー: クリップボードにデータを書き込む

writeText()

テキストをクリップボードにコピーするには、writeText() を呼び出します。この API は 非同期の場合、writeText() 関数は、エラーを解決または 渡されたテキストが正常にコピーされたかどうかに応じて拒否されます。

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

対応ブラウザ

  • 66
  • 79
  • 63
  • 13.1

ソース

write()

実際、writeText() は汎用 write() の便利なメソッドにすぎません。 メソッドを使用すると、画像をクリップボードにコピーできます。writeText() と同様に、 非同期であり、Promise を返します。

クリップボードに画像を書き込むには、その画像を blob。これを実現する 1 つの方法は、 これは、fetch() を使用してサーバーに画像をリクエストしてから、 次の日付の blob(): レスポンスが返されます。

サーバーからの画像のリクエストは、 理由もさまざまです。幸いなことに、キャンバスに画像を描画して、 キャンバスを呼び出して toBlob() メソッドを呼び出します。

次に、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);
}

対応ブラウザ

  • 66
  • 79
  • 127
  • 13.1

ソース

copy イベント

ユーザーがクリップボードのコピーを開始する場合 かつ preventDefault()呼び出さない場合、 copy 件の予定 clipboardData プロパティが含まれており、アイテムがすでに適切な形式になっている。 独自のロジックを実装する場合は、preventDefault() を呼び出して、 独自の実装を優先してデフォルトの動作を防止できます。 この場合、clipboardData は空になります。 たとえば、あるページにテキストと画像が含まれていて、ユーザーが クリップボードのコピーを開始した場合、カスタム ソリューションではテキストを破棄し、 画像をコピーします。以下のコードサンプルに示すように、これを実現できます。 この例には、前のスライドにフォールバックする方法は説明されていません。 (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 イベントの場合:

対応ブラウザ

  • 1
  • 12
  • 22
  • 3

ソース

ClipboardItem の場合:

対応ブラウザ

  • 76
  • 79
  • 127
  • 13.1

ソース

貼り付け: クリップボードからデータを読み取っている

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

対応ブラウザ

  • 66
  • 79
  • 125
  • 13.1

ソース

read()

navigator.clipboard.read() メソッドも非同期であり、 あります。クリップボードから画像を読み取るには、 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);
  }
}

対応ブラウザ

  • 66
  • 79
  • 127
  • 13.1

ソース

貼り付けたファイルの操作

次のようなクリップボードのキーボード ショートカットを使えると、 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());
});

対応ブラウザ

  • 3
  • 12
  • 3.6
  • 4

ソース

貼り付けイベント

前述したように、今後、Clipboard API と連携するイベントを導入する予定です。 現時点では、既存の paste イベントを使用できます。新しい クリップボードのテキストを読み取るための非同期メソッドです。copy イベントと同様、 preventDefault() の呼び出しを忘れる。

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

対応ブラウザ

  • 1
  • 12
  • 22
  • 3

ソース

複数の MIME タイプの処理

ほとんどの実装では、1 回のカットで複数のデータ形式をクリップボードに配置できます。 使用できます。これには 2 つの理由があります。アプリ デベロッパーとして、 テキストや画像のコピー先となるアプリの機能をユーザーが認識する手段がない 多くのアプリケーションでは、構造化データを書式なしテキストとして貼り付けることができます。これは通常 [編集] メニュー項目とともに [貼り付けと [スタイルに合わせる] または [書式なしで貼り付け] を選択します。

次の例は、これを行う方法を示しています。この例では、fetch() を使用して以下を取得します。 画像データだけでなく、さまざまな画像から <canvas> または File System Access API を使用できます。

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

セキュリティと権限

クリップボードへのアクセスは、常にブラウザのセキュリティ上の懸念事項となっています。なし すべての悪意のあるコンテンツを自動的にコピーできる 貼り付けると壊滅的な結果が生じるという問題がありました。 たとえば、rm -rf /解凍爆弾の画像 クリップボードにコピーできます。

<ph type="x-smartling-placeholder">
</ph> クリップボードへのアクセスをユーザーに求めるブラウザ プロンプト。 <ph type="x-smartling-placeholder">
</ph> Clipboard API の権限プロンプト。

ウェブページにクリップボードへの無制限の読み取りアクセス権を付与することで、 面倒な作業です。ユーザーはパスワードなどの機密情報を定期的にコピーし、 クリップボードにコピーしておけば、個人情報がなければ、 秘匿化できます。

多くの新しい API と同様に、Clipboard API は、 提供します。不正使用を防止するため、クリップボードへのアクセスはページが クリックします。アクティブなタブのページがクリップボードに書き込みできるように、 リクエストしていますが、クリップボードから読み取るには、常に 付与します。

コピーと貼り付けの権限が Permissions APIclipboard-write 権限は、ページがクロールされたときに自動的に付与されます。 クリックします。clipboard-read 権限をリクエストする必要があります。これを行うには、 クリップボードからデータを読み取ろうとします。次のコードは、後者を示しています。

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

また、切り抜きまたはフィルタの呼び出しにユーザーの操作が必要かどうかも allowWithoutGesture オプションを使用して貼り付けることもできます。この値のデフォルト ブラウザによって異なるため、必ず含めてください。

ここで、Clipboard API の非同期性がとても役に立ちます。 クリップボード データの読み書きを行おうとすると、自動的に まだ付与されていない場合は付与してください。API は Promise ベースのものなので これは完全に透過的であり、ユーザーがクリップボードの権限を拒否すると、 ページが適切に応答できるように、拒否するプロミスを約束する。

ブラウザではページがアクティブなタブの場合にのみクリップボードへのアクセスを許可するため、 ここに示されている例の一部は デベロッパー ツール自体がアクティブなタブであるため、ブラウザのコンソールに表示されます。コツはやはり先延ばしです。 setTimeout() を使用してクリップボードにアクセスし、ページ内をすばやくクリックして 関数が呼び出される前にフォーカスを合わせます。

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

権限ポリシーの統合

この API を iframe で使用するには、 権限ポリシー 選択的に有効化および有効化できるようにするメカニズムを ブラウザの各種機能や API を無効にできます。具体的には、環境変数、 アプリのニーズに応じて clipboard-readclipboard-write のいずれかまたは両方。

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

機能検出

すべてのブラウザをサポートしながら Async Clipboard API を使用するには、以下をテストします。 navigator.clipboard を実行し、以前のメソッドにフォールバックします。たとえば 他のブラウザを組み込むには貼り付けを実装できます。

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

それだけではありません。Async Clipboard API の登場以前は、 ウェブブラウザ間でさまざまなコピー&ペーストの実装を行う。ほとんどのブラウザでは ブラウザ独自のコピーして貼り付けは、 document.execCommand('copy')document.execCommand('paste')。テキストが DOM に存在しない文字列の場合は、 DOM と選択済み:

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

デモ

以下のデモで Async Clipboard API を試すことができます。Glitch での操作 テキスト デモをリミックス可能 または画像デモを使用して、 試してみてください。

最初の例では、クリップボードとの間でテキストを移動する例を示します。

画像で API を試すには、このデモを使用します。前に説明したように、サポートされているのは PNG のみです。 かつ、 いくつかのブラウザ

謝辞

Asynchronous Clipboard API は Darwin が実装しました HuangGary Kačmarčík。Darwin もデモを提供しました。 Kyarik、お手数をおかけしますが、Gary Kačmarčík が ご覧ください

ヒーロー画像作成者: Markus Winkler スプラッシュを解除