🦓

[Rails]クリップボードにコピーする

2023/09/14に公開

はじめに

技術系記事サイトによく見かけるクリック一つでクリップボードにコピーする機能を作っていきます。

ブラウザのClipboard APIを使います。
https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API

環境

Rails 7.0.7
ruby 3.2.1

流れ

  1. コードブロックを作成する
  2. コピーボタンを作成する
  3. JSコントローラーを作成する

Clipboard API

Clipboard API は、Webアプリケーションでクリップボード(コピー&ペーストの操作)にアクセスし、テキストやデータを読み取ったり書き込んだりするためのJavaScriptのAPIです。

  1. navigator.clipboard.writeText(text):
    テキストをクリップボードに書き込むためのメソッドです。指定したテキストがクリップボードにコピーされます。

  2. navigator.clipboard.readText():
    クリップボードからテキストを読み取るためのメソッドです。クリップボードにあるテキストを取得できます。

  3. イベント: clipboard.write および clipboard.read:
    書き込み操作や読み取り操作が完了したときに発生するイベントです。これらのイベントを使用して、操作の状態をウォッチしたり、成功または失敗を処理したりできます。

Clipboard API サンプルコード
// テキストをクリップボードにコピーする
navigator.clipboard.writeText("Hello, Clipboard!")
  .then(() => {
    console.log("テキストがクリップボードにコピーされました");
  })
  .catch((error) => {
    console.error("クリップボードへのコピーに失敗しました", error);
  });

// クリップボードからテキストを読み取る
navigator.clipboard.readText()
  .then((text) => {
    console.log("クリップボードから読み取ったテキスト:", text);
  })
  .catch((error) => {
    console.error("クリップボードからの読み取りに失敗しました", error);
  });

クリップボード API を使用するには、ブラウザが対応している必要があります。なので、導入されてないブラウザでは動作しません。

また、セキュリティ上の制限もあるため、特に読み取り操作はユーザーの許可が必要です。ユーザーの許可なしにクリップボードの内容を読み取ることはできません。

Navigatorはブラウザのコンテキストで使用されるJavaScriptのグローバルオブジェクトの1つです。これはウェブページ上で実行されるJavaScriptコードがブラウザの環境とやり取りするためなオブジェクトです。

Navigatorにはいろんなメソッドがありますが、今回はclipboardメソッドを使います。

コードブロックを作成する

<code></code>タグで囲むことで、ブラウザにそれがプログラムのコードであることを明示することができます。
また、<pre></pre>タグと合わせて使用することでコード内で整形した改行などを反映させることができます。

<pre>
 <code>
  <div>
   <h3><%= @user.email %></h3>
  </div>
 </code>
</pre>

コピーボタンを作成する

TailwindCSSとHeroicon使ってスタイリングします。
JSコントローラーを指定し、クリックに対してcopyアクションを追加します。

<%= button_tag '', 
       data: { controller: "clipboard,", 
       action: "click->clipboard#copy", 
       clipboard_content_value: @user.email } do %>
   <%= heroicon "clipboard-document", variant: :outline, 
       options: { class: "..." } %>
<% end %>

stimululusコントローラーを作成する

bin/rails g stimulus clipboard  
      create  app/javascript/controllers/clipboard_controller.js
app/javascript/controllers/clipboard_controller.js
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="clipboard"
export default class extends Controller {
  static values = {
    content: String
  }

  connect(){
    this.originalContent = this.element.innerHTML
  }
  
  copy() {
    console.log(this.element)
    navigator.clipboard.writeText(this.contentValue).then(
      () => {
        this.element.textContent = "コピーしました!"
        setTimeout(()=>{
          this.element.innerHTML = this.originalContent
        }, 2000)
      },
      () => {
        alert("クリップボードにコピー出来ませんでした。")
      }
    )
  }
}

最初に、ボタンのアイコンをthis.originalContent に保存しています。
そして、navigator.clipboard.writeText(this.contentValue) を使用して、this.contentValue に指定されたテキストをクリップボードにコピーします。
成功した場合、ボタンの本文を 「コピーしました!」 に変更し、2秒後に元のアイコンに戻します。
失敗した場合、アラートを表示してエラーメッセージをユーザーに通知します。

クリップボードの機能を確認します。
Image from Gyazo

終わりに

stimulus公式にチュートリアルを載せてるのでそちらも参考してみてください!
https://stimulus.hotwired.dev/handbook/building-something-real

Discussion