メールアドレスなどでよく見かける、「クリックしてクリップボードにコピー」
よく見かけるが故にcopy('コピーしたい文言')
みたいな感じで簡単に出来ると思ったら、意外と複雑だったので備忘録です。
clipboardCopy: function(e) {
e.preventDefault()
const targetSentence = 'コピーしたい文言'
let input = document.createElement('input')
input.readOnly = true
document.body.appendChild(input)
input.value = targetSentence
input.select()
document.execCommand('copy')
document.body.removeChild(input)
}
ざっくりとした流れ
①inputを生成する
let input = document.createElement('input')
②body内の一番最後の要素に、①のinputノードを追加する
document.body.appendChild(input)
③inputの中に、任意のコピーしたい文字列を挿入する
input.value = targetSentence
④inputの内容をselectする
input.select()
⑤選択している内容に対して、コピーコマンドを実行する
document.execCommand('copy')
⑥inputは用済みなのでさようならする
document.body.removeChild(input)
なぜinputか
select()
を使えるのがキモなんだと思います。
補足
execCommand()について
コマンドキーを押して出来ることがだいたいできるみたいです。
cutやpasteもできます。ただし、pasteはバックグラウンドのみ。
(個人情報などがクリップボードに入っていた場合勝手に抜き取られる可能性があるため。)
ちなみにIEだとpasteも許容しているみたいです。さすがですね。
https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard
ん?と思ったのは、これ非推奨なんですよね。
ほかにクリップボードとやりとりできる代替え手段があるのでしょうか?また調べてみます。
https://developer.mozilla.org/ja/docs/Web/API/Document/execCommand
時間がかかったところ
iOSで確認すると、クリックと同時に勝手にスクロールされる
iOSですと、input系を選択(タップ)した時にその要素がズームされたり、中央よりちょい上の位置にスクロールされたりします。
この例では、作成したinputをbodyの中の最後の子ノードのうしろに追加しているので、ページの最後にinputが出現している形になります。
そのため、テキストリンクをタップした瞬間に、ページ下方へのスクロールが走ります。
よかれと思ってやってくれているんだろうけども、inputはユーザが認識するよりも先に消えてしまうのでなんの意味もないです…。
ここで、inputをreadonlyに設定することで、スクロールを防ぎます。
input.readOnly = true
ズームされる
こちらはjsの問題ではなく、iOSの仕様と関係しています。
iOSですと16px未満のfont-sizeをもつinputをタップすると、問答無用でズームされます。
上記の実装でも目にはinputはみえませんが、input.selectをしているので謎にズームされます。
ズーム禁止にしてしまうとユーザビリティに影響が出るので、ひとまずSPでのfont-sizeを16pxにしてことなきを得ました。
(これを知ってたら最初から16pxにしてたのに…次回から気をつけよう)