JavaScriptによる画像のペースト
HipchatやSlackのWebアプリ版で便利な機能の一つとして、画像のペースト機能があります。 クリップボードに保持されている画像をテキスト入力欄でペーストし、アップロードすることができます。
JavaScriptでこの画像のペーストをどのようにやっているのか、その詳細について調べてみました。
tl;dr
Clipboard API
Clipboard APIを用いた画像のペースト
JavaScriptによる画像のペーストを実現する方法の一つとしてClipboard APIがあります。Clipboard APIはW3Cによって標準化が進められているクリップボードを扱うAPIです。 具体的な実装方法は以下のようになります。
// 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); }
- pasteイベントハンドラを定義し、中で
event.clipboardData.items
を参照する getAsFile()
を呼ぶことによってBlobオブジェクトを取得する- さらにFileReaderAPIを使用し、Blobオブジェクトが保有するバッファの中身を取得する。
event.clipboardDataについて
event.clipboardData
はDataTransfer
オブジェクトを返却します。DataTransferオブジェクトはブラウザのドラッグ&ドロップに用いられるオブジェクトです。
DataTransfer.items
は更にドラッグデータストアのアイテム一覧を保持するDataTransferItemList
オブジェクトを返却します。DataTransferItemListから更に各ドラッグデータストアアイテムDataTransferItem
にアクセスすることができます。
このDataTransferItem
にクリップボードに保持された画像データが格納されています。
参考:
event.clipboardData.itemsがブラウザによって存在しない
実はevent.clipboardData.items
、つまりDataTransfer.items
インターフェースの実装がブラウザによってまちまちです。
確認したところ、SafariやFirefoxでは実装されていません...
906420 – [DnD] dataTransfer.items undefined in Firefox
じゃぁ画像のペーストをSafariやFirefoxでどうやればいいのか?HipchatやSlackではどうやって対応してるのでしょうか?
HipchatとSlackの対応:対応ブラウザのみの実装
HipchatやSlackではevent.clipboardData.items
を用いています。そのためSafariやFirefoxではエラーがでるという...潔い!
HipchatやSlackの画像のコピペ便利だなーっておもってて調べてたんだけど、Slackは普通にevent.clipboardData.items使ってるみたいだった。safariだとうまくいかん pic.twitter.com/Nz9Exn8OLt
— んむて (@nnm_tech) May 2, 2015
paste.jsによるFirefoxの対応
paste.jsと呼ばれるJavaScriptライブラリを用いるとFirefoxでも画像のペーストが可能になります。
paste.jsはFirefoxの場合、内部でcontentEditable 属性を用いたトリッキーな手法で画像のペーストを実現しています。contentEditable属性とはWeb コンテンツの内容をユーザーから編集可能にするものです。contentEditable属性がtrueの場合、コンテンツが編集可能となった領域はリッチテキストをサポートし、リンクや画像を挿入することができます。
paste.jsではcontentEditable属性がtrueのhiddenなdiv要素を用意します。その後pasteイベントが発火した際にdiv要素に画像を一旦貼り付けることにより画像データを取得します。頭いい!
参考:
- monoe's blog - [HTML5API] contentEditable 属性を使用したリッチテキストエディタの実現
- contenteditable 属性 - グローバル属性 - HTML5 タグリファレンス - HTML5.JP
まとめ
今回SlackやHipchatで頻繁に使う画像のペースト機能について調べてみました。Clipboard APIは現状ブラウザによって対応が異なるため、注意が必要そうです。文字と一緒に画像を添付して投稿するWebアプリは少なくないと思うので、そんなシステム達が今後より便利になるといいですね。