JavaScriptによる画像のペースト

f:id:koyamay:20150511061806p:plain

HipchatやSlackのWebアプリ版で便利な機能の一つとして、画像のペースト機能があります。 クリップボードに保持されている画像をテキスト入力欄でペーストし、アップロードすることができます。

JavaScriptでこの画像のペーストをどのようにやっているのか、その詳細について調べてみました。

tl;dr

  • Clipboard API
  • Hipchat, Slackにおけるペースト機能
  • paste.jsによるFirefoxの対応

Clipboard API

Clipboard APIを用いた画像のペースト

JavaScriptによる画像のペーストを実現する方法の一つとしてClipboard APIがあります。Clipboard APIW3Cによって標準化が進められているクリップボードを扱うAPIです。 具体的な実装方法は以下のようになります。

stackoverflow.com

// window.addEventListener('paste', ... or
document.onpaste = function(event){
  var items = (event.clipboardData || event.originalEvent.clipboardData).items;
  console.log(JSON.stringify(items)); // will give you the mime types
  var blob = items[0].getAsFile();
  var reader = new FileReader();
  reader.onload = function(event){
    console.log(event.target.result)}; // data url!
  reader.readAsDataURL(blob);
}

Edit fiddle - JSFiddle

  1. pasteイベントハンドラを定義し、中でevent.clipboardData.itemsを参照する
  2. getAsFile()を呼ぶことによってBlobオブジェクトを取得する
  3. さらにFileReaderAPIを使用し、Blobオブジェクトが保有するバッファの中身を取得する。

event.clipboardDataについて

event.clipboardDataDataTransferオブジェクトを返却します。DataTransferオブジェクトはブラウザのドラッグ&ドロップに用いられるオブジェクトです。

DataTransfer.itemsは更にドラッグデータストアのアイテム一覧を保持するDataTransferItemListオブジェクトを返却します。DataTransferItemListから更に各ドラッグデータストアアイテムDataTransferItemにアクセスすることができます。 このDataTransferItemクリップボードに保持された画像データが格納されています。

参考:

event.clipboardData.itemsがブラウザによって存在しない

実はevent.clipboardData.items、つまりDataTransfer.itemsインターフェースの実装がブラウザによってまちまちです。 確認したところ、SafariFirefoxでは実装されていません...

906420 – [DnD] dataTransfer.items undefined in Firefox

じゃぁ画像のペーストをSafariFirefoxでどうやればいいのか?HipchatやSlackではどうやって対応してるのでしょうか?

HipchatとSlackの対応:対応ブラウザのみの実装

HipchatやSlackではevent.clipboardData.itemsを用いています。そのためSafariFirefoxではエラーがでるという...潔い!

paste.jsによるFirefoxの対応

paste.jsと呼ばれるJavaScriptライブラリを用いるとFirefoxでも画像のペーストが可能になります。

github.com

paste.jsはFirefoxの場合、内部でcontentEditable 属性を用いたトリッキーな手法で画像のペーストを実現しています。contentEditable属性とはWeb コンテンツの内容をユーザーから編集可能にするものです。contentEditable属性がtrueの場合、コンテンツが編集可能となった領域はリッチテキストをサポートし、リンクや画像を挿入することができます。

paste.jsではcontentEditable属性がtrueのhiddenなdiv要素を用意します。その後pasteイベントが発火した際にdiv要素に画像を一旦貼り付けることにより画像データを取得します。頭いい!

参考:

まとめ

今回SlackやHipchatで頻繁に使う画像のペースト機能について調べてみました。Clipboard APIは現状ブラウザによって対応が異なるため、注意が必要そうです。文字と一緒に画像を添付して投稿するWebアプリは少なくないと思うので、そんなシステム達が今後より便利になるといいですね。