Garbanzo Note

Vue.js カスタムディレクティブでクリップボードコピーを実装してみる

November 26, 2020

この記事は1年以上が経過しています。内容が古い可能性があります。

目次


カスタムディレクティブとは?

カスタムディレクティブ — Vue.js

Vue.js 本体で出荷されたディレクティブの標準セットに加えて (v-model と v-show)、カスタムディレクティブ (custom directive) を登録することができます。Vue 2.0 では、コードの再利用と抽象化における基本の形はコンポーネントです。しかしながら、通常の要素で低レベル DOM にアクセスしなければならないケースがあるかもしれません。

まずディレクティブとは、描画されたDOMに特定のリアクティブな振舞いを与えるもの

v-の接頭辞が付き、Vue.jsの標準セットではv-modelv-ifv-forなどが該当する

カスタムディレクティブとは、そのディレクティブを(v-xxx)を自作して登録出来る仕組みのことである


クリップボードコピーを実装してみる

仕様ととしては

  • v-clip:[イベント名]="コピーする値"で利用出来るように実装する
  • イベントはデフォルトClickイベントとする(v-clip="コピーする値")

e.g.

<button v-clip="message" /> //Click時にmessageがコピーされる
<input v-clip:focus="message" /> //フォーカス時にコピーされる
<input v-clip:change="message" /> //更新時にコピーされる

//script
message = "Hello!"

クリップボードにコピーするには?

クリップボード APIを使用することでクリップボードにコピーが可能

クリップボード API - Web API | MDN

navigator.clipboard.writeText('コピーする値');

あとは、この処理をaddEventListenerに登録すればOK

directiveを書く

今回はサンプルでVue CLIで雛形生成してsrc/main.tsに定義している

グローバル登録とする為、Vue.directive関数を使用する

(※コンポーネント単位で登録も可能)

bindフック関数を用いて実装

Vue.directive("clip", {
  bind: (el, binding) => {
    const event = binding.arg || "click";
    el.addEventListener(event, () => {
      navigator.clipboard.writeText(binding.value);
    });
  }
});

bindフック関数について

bind: ディレクティブが初めて対象の要素にひも付いた時に 1 度だけ呼ばれます。ここで 1 回だけ実行するセットアップ処理を行えます 引用: arg: もしあれば、ディレクティブに渡される引数。例えば v-my-directive:foo では、arg は “foo” です。

ディレクティブフック引数(bindの引数)について

  • el: ディレクティブがひも付く要素。DOM を直接操作するために使用できます。
  • binding: 以下のプロパティを含んでいるオブジェクト。

    • value: ディレクティブに渡される値。例えば v-my-directive=“1 + 1” では、value は 2 です。
    • arg: もしあれば、ディレクティブに渡される引数。例えば v-my-directive:foo では、arg は “foo” です。

ここでのargは、イベントをとる形にしている。

v-clip:focusであればfocusがとれる。addEventListenerのイベントとして登録

これで描画してクリップボードにコピーすることが出来る

しかし、これだとコピーする値(data)が更新された時に対応が出来ず、初期値がコピーされる状態になってしまっている

updateフック関数を使い更新に対応する

Vue.directive("clip", {
  bind: (el, binding) => {
    const event = binding.arg || "click";
    el.addEventListener(event, () => {
      navigator.clipboard.writeText(binding.value);
    });
  },
  // 以下追加
  update: (el, binding) => {
    const event = binding.arg || "click";
    el.addEventListener(event, () => {
      navigator.clipboard.writeText(binding.value);
    });
  }
});

updateフック関数の部分を追加

update: ひも付いた要素を抱合しているコンポーネントの VNode が更新される度に呼ばれます。しかし、おそらく子コンポーネントが更新される前でしょう。 ディレクティブの値が変化してもしなくても、バインディングされている値と以前の値との比較によって不要な更新を回避することができます。(フック引数に関しては下記を参照してください)

これでコピーする値が更新されてもちゃんと更新した値がクリップボードにコピーされる

これでもいいのだが、bindupdateの内容が同一で冗長なのでいけていない

「関数による省略記法」を用いて省略する

directive関数の第二引数に関数を渡すことでbindupdateを省略することが可能

多くの場合、bind と update には同じ振舞いが欲しいでしょうが、その他のフックに関しては気にかけません。 カスタムディレクティブ — Vue.js

最終版

Vue.directive("clip", (el, binding) => {
  const event = binding.arg || "click";
  el.addEventListener(event, () => {
    navigator.clipboard.writeText(binding.value);
  });
});

まとめ

フック関数やディレクティブフック引数を把握しないといけないが、

簡単にクリップボードコピーのカスタムディレクティブの実装出来たと思う。

他にも何か作ってみたい。


Garbanzo

Webエンジニアの備忘録です。 学んだことをアウトプットしています。

合計記事数
25