[Vue.js]Clipboard APIを使ってOSのClipboardにアクセスする。

# はじめに

Vue.jsアプリケーションからクリップボードにアクセスする必要があったので、クリップボードAPIを使ったサンプルコードを作成してみました。クリップボードAPIの詳細は以下のリンクを参考にしてください。尚VueはV2系を使用しています。

https://developer.mozilla.org/ja/docs/Web/API/Clipboard_API

# 実装した処理

今回、要件としてはクリップボードへの書き込みは不要でしたので、クリップボードからの読み込みのみを実装しています。（クリップボードへの書き込みを行う場合はClipboard.writeText()で可能です。）

なおClipboard APIですがブラウザによって実装状況がかなり異なるため、処理をデバイス、ブラウザによって変更しています。ブラウザ、デバイスの切り分けには[UAParser.js](https://github.com/faisalman/ua-parser-js)を利用しています。[^1]

[^1]:UAParaserは過去にマルウェアが混入したバージョンが存在します。必ず安全なバージョンを利用してください。(https://www.security-next.com/131039)

# ソースコード

```vuejs
<template>
  <div id="app">
    <button v-on:click="showMessage">ボタン</button>
    <p>{{ msg }}</p>
  </div>
</template>

<script>
var parser = require("ua-parser-js");

export default {
  data() {
    return {
      name: "app",
      msg: "",
      currentfocus: false,
    };
  },
  created: function () {
    window.addEventListener("focus", this.focus);
    window.addEventListener("blur", this.blur);
  },
  destroyed: function () {
    window.removeEventListener("focus", this.focus);
    window.removeEventListener("blur", this.blur);
  },
  methods: {
    async showMessage() {
      var ua = parser(window.navigator.userAgent);

      if (ua.device.type == "mobile" || ua.device.type == "tablet") {
        if (ua.browser.name == "Safari" || ua.browser.name == "Mobile Safari") {
          this.msg = await navigator.clipboard.readText();
        }else{
          this.msg = "Not supported"
        }
      } else {
        if (ua.browser.name === "Chrome" || ua.browser.name == "Edge") {
          let newmsg = "";
          setInterval(async () => {
            try {
              if (this.currentfocus == true) {
                newmsg = await navigator.clipboard.readText();
                if (newmsg != this.msg) {
                  this.msg = newmsg;
                }
              }
            } catch (err) {
              console.log(err);
            }
          }, 5000);
        } else if (/Safari/.test(ua.browser.name)) {
          this.msg = await navigator.clipboard.readText();
        }
      }
    },
    focus() {
      this.currentfocus = true;
    },
    blur() {
      this.currentfocus = false;
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

```
# 処理の説明
（１）PC版Chrome、Edgeの処理

PC版のChrome及びEdgeは一度クリップボードのパーミッションを取得すると、その後パーミッションの取得は不要となるので、タイマーで５秒ごとに監視を行い、変化があれば画面に出力しています。なおdocumentにフォーカスがあたっていない状態でクリップボードにアクセスするとエラーが発生するため、focusとblurイベントでフォーカスの状態を取得しています。

（２）Safariの処理

Safariの場合read処理時に「paste」というダイアログが毎回出力されるようです。この為、ボタン押下時にのみクリップボードの中身を出力する処理を実装しています。ボタン押下後、「paste」というダイアログが出力しダイアログを押すと画面に出力されます。

(3）Mobile版 Chrome(Webkit)

現在iOS上のChrome及びGoogle BrowserはClipboardに対応していない模様です。[こちら](https://stackoverflow.com/questions/62327358/javascript-clipboard-api-safari-ios-notallowederror-message)によるとUserEventでの呼び出しのみ対応となっていましたが、どうも動作せず、今回は"not supported"を出力しています。

# 残課題

Android上のClipboard APIのサポート状況をが未調査なので、今後確認する予定です。ClipBoard APIの挙動はブラウザによって大きく異なる為、ブラウザ毎に挙動の調査をする必要があり、同様の処理実装が必要な方の為に現在の実装状況をまとめてみました。