JavaScriptからOSのクリップボードを制御する


概要
====

通常、JavaScriptからクリップボードを制御することはできないが、Flashを介在させることでクリップボードへのコピーを実現することはできる。ここでは、ZeroClipboard Rails を利用して実現する方法を解説する。(この解説では Ruby  on Rails を前提としているが、フレームワークに依存しない [ZeroClipboard](https://github.com/zeroclipboard/zeroclipboard) を利用することもできる。)

環境
====

- サーバ側
  - Ruby on Rails 4.0.2
  - [ZeroClipboard Rails](https://github.com/zeroclipboard/zeroclipboard-rails) 0.0.10
- クライアント
  - Mozilla Firefox (Mac, Windows)
  - Google Chrome (Mac, Windows)
  - Safari (Mac)
  - Microsoft Internet Explorer 9
  - ※いずれも、Adobe Flash Playerをインストール済

注意
====

ZeroClipboard は透明のFlash movie (SWF) を貼り付け、それをユーザがクリックすることでクリップボードへの書き込みを実現している。このため、以下の制限がある

- **自由自在にクリップボードに書き込めるわけではない**
- ユーザの **クリックが必要** なので、インターフェイスが制限される
- 当然ながらFlashがインストールされていない環境では利用することができない

準備
====

Rails のアプリケーションをセットアップする。

```
rails new MoeApp
vi Genfile
bundle install
```

Gemfile には以下の内容を追加する。

```ruby:Gemfile
gem "bootstrap-sass"
gem "haml"
gem "zeroclipboard-rails"
```

application.js に以下を追加。

```javascript:app/assets/javascripts/application.js
//= require zeroclipboard

$(function() {
  // ZeroClipboard
  var clip = new ZeroClipboard();
  clip.glue($(".copy-to-clipboard"));

  clip.on("dataRequested", function (client, args) {
    var msg = "Copied to clipboard!";
    alert(msg);
  });
});
```

これで、`copy-to-clipboard` クラスを持つ要素はクリップボードへのコピー機能を持つようになる。

設定
====

`copy-to-clipboard` クラスが付いた要素をクリックすると、データがクリップボードにコピーされる。コピーされるデータは `data-clipboard-text` (テキストを入れる) または `data-clipboard-target` (対象のID) で指定したものとなる。

data-clipboard-text を使用
----

```haml
%a.copy-to-clipboard(href="#" data-clipboard-text="にゃんぱすー")
  なのん
```

data-clipboard-target
----

```haml
%button.btn.btn-default.copy-to-clipboard(data-clipboard-target="nonnon")
  なのん
%p#nonnon<
  にゃんぱすー
```

問題とその対応
====

マニュアルに書いてあるけど、きちんと読まずにハマった。

onmouseover, onmouseout が動作しない
----

次のようなコードは上手く動かないことがある。

```javascript
$("a.copy-to-clipboard").on("mouseover", function { ... });
$("a.copy-to-clipboard").on("mouseout", function { ... });
```

代わりに、ZeroClipboard のインスタンス (ここでは `clip`) を介してイベントハンドラを操作する必要がある。

```javascript
var clip = new ZeroClipboard();
clip.glue($(".copy-to-clipboard"));
clip.on("mouseover", function { ... });
clip.on("mouseout", function { ... });
```

:hover が有効にならない
----

直接 :hover を利用することができないので、たとえば Bootstrap の btn や dropdown-menu を利用した際にはその項目だけ背景が変化しないなど少し悲しいことに。以下は hover が有効になっていない例。

![zeroclipboard-hover-will-not-work.png](https://qiita-image-store.s3.amazonaws.com/0/10045/85fecfbc-c4ce-9d31-9663-9e6d32b9b3fd.png "zeroclipboard-hover-will-not-work.png")


Hover, active になった際には、`zeroclipboard-is-hover` または `zeroclipboard-is-active` クラスが追加されるため、これを利用して擬似的に実現することはできる。たとえば、bootstrap-sass を利用している場合には、以下のように設定すると良い。

```css:application.css.scss
@import "bootstrap";

.btn.zeroclipboard-is-hover {
  @extend .btn:hover;
}

.btn-default.zeroclipboard-is-hover {
  @extend .btn-default:hover;
}

.dropdown-menu > li > .zeroclipboard-is-hover {
  color: $dropdown-link-hover-color;
  background-color: $dropdown-link-hover-bg;
}
```


まとめ
====

- ZeroClipboard を使うことでクリップボードに書き込むことは可能になる。
- Flashを利用しているため、スタイルシートやJavaScriptの書き方やインターフェイス設計に若干の工夫が必要。
- [のんのんびより](http://nonnontv.com/) が来週で終わってしまうので、生きているのが辛い。