移転しました。

約3秒後に自動的にリダイレクトします。

WebでHTMLをいい感じにコピーさせる

こんにちは id:mstssk です。

Webサービスを作っていると、何かしらをユーザーにコピーして使ってもらうというシチュエーションが出てきます。 WebページをシェアするためにURLのコピーボタンが置いてある、なんていうのはよく見かけますね。

しかし、ある程度複雑なコンテンツはHTML情報としてコピーさせたい事が稀にあります。 画像やリンクを含んだHTMLです。

そこで、もう少し欲張って、こういうことが出来ないか調べてみました。

3行まとめ

  • ただのテキストをコピーするだけならクリップボードAPIを使えばOK。
  • HTMLをWYSIWYGに貼り付ける状態でコピーさせるには、現在非推奨な実装方法を使いつつ一工夫必要になるけど可能。
  • 一行余った
動作サンプル

stackblitz.com

以下、解説。

Webブラウザクリップボード API

developer.mozilla.org

最近のWebブラウザにはPCのクリップボードを操作するAPIが標準搭載されており、JavaScriptを数行書くだけで任意のテキストをコピーさせられます。

// 「ほげほげ」というテキストをコピーする場合
copyButton.addEventListener("click", () => {
  navigator.clipboard.writeText("ほげほげ")
    .then(()=> console.log("copied!"));
});

好き勝手にクリップボードを操作できるわけではなく制約があります。 ユーザーがボタンをクリックしたりとか何かしら操作を行った時だけクリップボードへのアクセスが許可されています。

このクリップボード APIはどのWebブラウザにもある機能ですが、2020年5月現在ではテキストまたは画像ファイルしかコピーさせられません

2020/07/10 追記

Safari 13.1.1ではクリップボード APIでtext/htmlのコピーが行えるようです。

クリップボードAPIの制限については、執筆時点のものであり、またブラウザごとに対応状況も違っています。 この記事を参考にする前にクリップボードAPIの最新の実装状況を確認するのをおすすめします。

HTMLコピーを無理やり実現するやり方

Hackyなやり方ですが、Chrome, Safari, Firefoxで動く実装はあります。

developer.mozilla.org

document.execCommand("copy"); でコピー動作を直接呼び出す方法は、クリップボードAPIが登場する以前はずっと使われてきましたが、現在では非推奨とされています。 しかし、今回はクリップボードAPIで未対応のデータ(HTML)を扱うため致し方ありません。

実際の実装は以下の通り。 行っているのは、ユーザーがマウスでWebページ内を範囲選択してコピーしているのと同じ事をJavaScriptからやっているだけ。 ただし、プレーンテキストの情報も持たせるために一工夫しています。

const html = `<a href="https://example.com/">
                <img src="https://github.com/viibar.png">
              </a>`;
copyButton.addEventListener("click", () => {
  // コピー対象にするダミー要素
  const elem = document.createElement("span");
  elem.style.height = "0px";
  elem.style.width = "0px";
  elem.innerHTML = html; // ダミー要素の中身が無いとSafariで動作しないので。
  document.body.appendChild(elem);

  // ダミー要素を選択状態にする
  const range = document.createRange();
  const selection = document.getSelection();
  selection.removeAllRanges();
  range.selectNodeContents(elem);
  selection.addRange(range);

  // コピーが行われたとき、コピーデータをすげ替える。ここが一工夫。
  document.addEventListener("copy", function listener(event) {
    event.preventDefault();
    event.clipboardData.setData("text/html", html); // for WYSIWYG
    event.clipboardData.setData("text/plain", html);
    document.removeEventListener("copy", listener);
    console.log("copied!", event);
  });

  // コピー実行
  document.execCommand("copy");

  // 選択状態解除とダミー要素削除
  selection.removeAllRanges();
  document.body.removeChild(elem);
});
実際に貼り付けてみた場合のスクリーンショット

WYSIWYGエディタやMS Wordなんかは、HTMLを解釈してそういうコンテンツとして貼り付けられます。

Gmailのメール作成画面(WYSIWYG) MS Word
Gmailのメール作成画面 Wordの画面

WYSIWYGエディタやWordにただのテキストとして貼り付けたいときは、右クリックメニューにオプションがあるはずです。

ただのテキストエディタでは、HTMLのソースコードをテキストとして貼り付けられます。

Visual Studio Code
Visual Studio Code