JavaScript は貼り付けイベントでクリップボードデータを取得します (クロスブラウザ) 質問する

JavaScript は貼り付けイベントでクリップボードデータを取得します (クロスブラウザ) 質問する

Web アプリケーションはどのようにして貼り付けイベントを検出し、貼り付けるデータを取得できるのでしょうか?

テキストをリッチ テキスト エディターに貼り付ける前に、HTML コンテンツを削除したいと思います。

貼り付けた後にテキストをクリーンアップすることはできますが、以前の書式設定がすべて失われるという問題があります。たとえば、エディターで文章を書いて太字にすることはできますが、新しいテキストを貼り付けると、すべての書式設定が失われます。貼り付けたテキストだけをクリーンアップし、以前の書式設定はそのままにしておきたいのです。

理想的には、ソリューションはすべての最新ブラウザ (MSIE、Gecko、Chrome、Safari など) で動作する必要があります。

MSIE には がありますclipboardData.getData()が、他のブラウザでは同様の機能は見つかりませんでした。

ベストアンサー1

ソリューション 1 (プレーンテキストのみ、Firefox 22 以降が必要)

IE6+、FF 22+、Chrome、Safari、Edge で動作します (IE9+ でのみテストされていますが、それより低いバージョンでも動作するはずです)

HTML の貼り付けまたは Firefox <= 22 のサポートが必要な場合は、ソリューション 2 を参照してください。

function handlePaste(e) {
  var clipboardData, pastedData;

  // Stop data actually being pasted into div
  e.stopPropagation();
  e.preventDefault();

  // Get pasted data via clipboard API
  clipboardData = e.clipboardData || window.clipboardData;
  pastedData = clipboardData.getData('Text');

  // Do whatever with pasteddata
  alert(pastedData);
}

document.getElementById('editableDiv').addEventListener('paste', handlePaste);
<div id='editableDiv' contenteditable='true'>Paste</div>

JSFiddle

このソリューションでは、関数に非標準のパラメータ「Text」を使用していることに注意してくださいgetData。ただし、執筆時点ではすべてのブラウザで動作します。


ソリューション 2 (HTML、Firefox <= 22 で動作)

IE6+、FF 3.5+、Chrome、Safari、Edgeでテスト済み

var editableDiv = document.getElementById('editableDiv');

function handlepaste(e) {
  var types, pastedData, savedContent;

  // Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
  if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {

    // Check for 'text/html' in types list. See abligh's answer below for deatils on
    // why the DOMStringList bit is needed. We cannot fall back to 'text/plain' as
    // Safari/Edge don't advertise HTML data even if it is available
    types = e.clipboardData.types;
    if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) {

      // Extract data and pass it to callback
      pastedData = e.clipboardData.getData('text/html');
      processPaste(editableDiv, pastedData);

      // Stop the data from actually being pasted
      e.stopPropagation();
      e.preventDefault();
      return false;
    }
  }

  // Everything else: Move existing element contents to a DocumentFragment for safekeeping
  savedContent = document.createDocumentFragment();
  while (editableDiv.childNodes.length > 0) {
    savedContent.appendChild(editableDiv.childNodes[0]);
  }

  // Then wait for browser to paste content into it and cleanup
  waitForPastedData(editableDiv, savedContent);
  return true;
}

function waitForPastedData(elem, savedContent) {

  // If data has been processes by browser, process it
  if (elem.childNodes && elem.childNodes.length > 0) {

    // Retrieve pasted content via innerHTML
    // (Alternatively loop through elem.childNodes or elem.getElementsByTagName here)
    var pastedData = elem.innerHTML;

    // Restore saved content
    elem.innerHTML = "";
    elem.appendChild(savedContent);

    // Call callback
    processPaste(elem, pastedData);
  }

  // Else wait 20ms and try again
  else {
    setTimeout(function() {
      waitForPastedData(elem, savedContent)
    }, 20);
  }
}

function processPaste(elem, pastedData) {
  // Do whatever with gathered data;
  alert(pastedData);
  elem.focus();
}

// Modern browsers. Note: 3rd argument is required for Firefox <= 6
if (editableDiv.addEventListener) {
  editableDiv.addEventListener('paste', handlepaste, false);
}
// IE <= 8
else {
  editableDiv.attachEvent('onpaste', handlepaste);
}
<div id='div' contenteditable='true'>Paste</div>

JSFiddle

説明

onpasteのイベントにはdiv関数handlePasteがアタッチされており、貼り付けイベントのオブジェクトという単一の引数が渡されますevent。特に興味深いのは、clipboardDataIE 以外のブラウザでクリップボードへのアクセスを可能にするこのイベントのプロパティです。IE では同等のものは ですwindow.clipboardDataが、これは API が若干異なります。

以下のリソースセクションを参照してください。


関数handlepaste

この機能には 2 つのブランチがあります。

最初は の存在を確認しevent.clipboardData、そのtypesプロパティに 'text/html' (メソッドを使用して確認される、または メソッドを使用して確認される文字列のtypesいずれか) が含まれているかどうかを確認します。これらの条件がすべて満たされている場合は、ソリューション 1 と同様に進めますが、'text/plain' の代わりに 'text/html' を使用します。これは現在、Chrome および Firefox 22 以降で動作します。DOMStringListcontainsindexOf

この方法がサポートされていない場合(他のすべてのブラウザ)、

  1. 要素の内容をDocumentFragment
  2. 要素を空にする
  3. waitForPastedData関数を呼び出す

関数waitforpastedata

この関数は、最初に貼り付けられたデータをポーリングします (20 ミリ秒ごとに 1 回)。これは、データがすぐに表示されないために必要です。データが表示されると、次の処理が実行されます。

  1. 編集可能なdivのinnerHTML(貼り付けられたデータ)を変数に保存します。
  2. DocumentFragmentに保存されたコンテンツを復元します
  3. 取得したデータを使用して「processPaste」関数を呼び出します

関数processpaste

貼り付けたデータに対して任意の処理を行います。この場合は、データに警告を出すだけなので、好きなように操作できます。貼り付けたデータに対して、何らかのデータ サニタイズ プロセスを実行する必要があるかもしれません。


カーソル位置の保存と復元

実際の状況では、選択内容を事前に保存し、後で復元したいと考えるでしょう(contentEditable <div> にカーソル位置を設定する)。その後、ユーザーが貼り付け操作を開始したときにカーソルがあった位置に、貼り付けたデータを挿入できます。

MDN のリソース

感謝ティム・ダウンDocumentFragmentの使用を提案し、clipboardData.typesの文字列の代わりにDOMStringListを使用したためにFirefoxでエラーをキャッチできるようにしました。

おすすめ記事