2020年3月18日水曜日

JavaScript で Clipboard の現状まとめ

JavaScript で Clipboard を操作しようとすると、本当によくハマる。 セキュリティ意識が高まってきた関係で API が山のようにあってコロコロ変わる。 ようやく多少の方向性が見えた気がするので、情勢をメモしておきます。

1. Flash経由でクリップボード操作

JavaScript にクリップボード操作の API が何もなかった時代には、Flash でクリップボードを操作するという技が使われていました。 しかし Flash が完全に死滅しつつある現在、この技は完全に使えないものになりました。

2. execCommand

その代わりに出てきたのが、execCommand です。 しかし昨今のセキュリティ事情の変化により、動かない利用用途も出てきています。 また「選択範囲を」「現在の場所に」などの制約があるので複雑なことには使えません。
  • document.execCommand("copy"): 選択範囲をクリップボードにコピー
  • document.execCommand("cut"): 選択範囲をクリップボードにカット
  • document.execCommand("paste"): クリップボードを現在の場所にペースト

3. copy/cut/paste Event

ユーザのイベントを補足して処理することもできます。 ここが一番の落とし穴で、サポート状況見ると動きそうだけど、これ昨今のセキュリティ事情の変化を受けてか、contentEditable なフィールド以外だと、Chrome が全然動かないんですよ。 一番わかりやすい例が これ ですが、動かない。 Firefox ではまだ動いたり流動的な情勢で、あまり深いことは言及を避けたほうが良いけど、いずれ動かなくなると予想します。
  • document.addEventListener("copy")
  • document.addEventListener("cut")
  • document.addEventListener("paste")

4. navigator.clipboard

ではどうするのかというと、navigator.clipboard を使います。 ユーザにクリップボードの利用許可を得てから、クリップボードを操作する API です。 いつできたは正確にはわからないのですが、Chrome 66 くらいからに見える。 async なイベントなのでこれまでの実装とは大きく異なる上、まだ実装も揺れ動いているみたいで Chrome 80 で実装方法が変わりました。

read, readText, write, writeText の 4 つのメソッドがあり、readText と writeText は安定してきているみたい。 問題は read, write のほうで、テキスト以外のクリップボード操作もできて強力なのですが、まだブラウザ実装も少なく、揺れ動いています。 Chrome の古い実装例ならいくつかまとまっているのですが、新しい実装方法はまだ解説記事も少なく、この記事くらいしか見当たらなかったので、自分なりの実装をメモしておきます。

以下はクリップボードに画像があるとき、その画像を canvas に描くサンプルです。 ちなみに Chrome 80 以上でないと動きませんし、執筆時点では Firefox も Safari も動きません。 今のところ Firefox, Safari は copy/cut/paste Event で実装したり、諦めるなどの回避策を取る必要があります。
document.getElementById('pasteButton').addEventListener('click', function() {
  navigator.clipboard.read().then(function(data) {
    for (let i=0; i<data.length; i++) {
      const img = data[i];
      console.log(img);
      for (const type of img.types) {
        if (type.indexOf('image') != -1) {
          img.getType(type).then(function(blob) {
            const img = new Image();
            img.onload = function() {
              const uploadCanvas = document.getElementById('uploadCanvas');
              uploadCanvas.width = img.width;
              uploadCanvas.height = img.height;
              uploadCanvas.getContext("2d").drawImage(img, 0, 0, uploadCanvas.width, uploadCanvas.height);
            }
            img.src = URL.createObjectURL(blob);
          });
        }
      }
    }
  });
});



navigator.clipboard は強力で良さげな API に思います。 とは言えセキュリティ意識の高まりを受けて、ブラウザの実装がまた混沌としてきた気がします。 しばらく実装が面倒そう。

0 件のコメント:

コメントを投稿