久しぶりに技術的な内容です。
「ボタンを押してある文字列をコピーさせる」機能は使う機会が多そうですが、Javascriptでサクッと実装することは出来ません。
ここではJavascriptを使ったコピー機能の実装方法を解説します。
任意の文字列をコピーさせる、というのはセキュリティ的な懸念があるようで、ネイティブな関数では実装されていませんでした。
Flashを使う方法が広く利用されていましたが、Flashをサポートしないブラウザが主流となった今では使用がはばかられます。
そんな中、HTML5でClipboard APIが実装されました。
https://developer.mozilla.org/ja/docs/Web/API/ClipboardEvent/clipboardData
今回目指す任意の文字列のコピーに一見すると使えそうですが、「コピーや切り取り時に発動」という仕様で、用途が限られて使えません。
利用例を可否別に以下に挙げてみます。
ネイティブには解決できない状況ですが、JavascriptのexecCommandを使うことで、任意の文字列をコピーさせる機能を実装可能です。
textareaやinputの文字列を選択した状態で、document.execCommand("copy")を実行すると、選択中の文字列をコピーできます。これが本来の使い方です。
これを利用し、次の流れでコピー機能を実現します。
inpuやtextarea内に設置した文字を自動選択させる方法を2つ試しました(以下でtextareaはDOMのノード)。
各ブラウザでテストした結果は以下です。
併用しても弊害が無いことから、今回は無理にユーザーエージェントで判定せず、両方を実行することとしました。
環境 | テスト結果 | ||||
端末・OS | ブラウザ | バージョン | 1.select() | 2.range | 1と2の両方 |
iOS 11.4 | Safari | Webkit 605.1.15 | × | ○ | ○ |
Chrome | 67.0.3396.69 | × | ○ | ○ | |
Android 6.0 | Chrome | 66.0.3359.158 | ○ | × | ○ |
Web View | 66.0.3359.158 | ○ | × | ○ | |
Windows | Edge | 42.17134.1.0 | ○ | ○ | ○ |
IE11 | 11.112.17134.0 | ○ | ○ | ○ | |
Chrome | 67.0.3396.87 | ○ | × | ○ | |
Firefox | 60.0.2 | ○ | ○ | ○ | |
Mac OSX | Safari | 11.1 | ○ | ○ | ○ |
テキストエリアを自動で設置&削除しますが、バックグランドで気づかれずに実行できるのが理想なので、テキストエリアは見えないようにします。
テキストエリアをどのようにして非表示化すれば実行できるのか、次の3つのCSSを調査しました。
各ブラウザでテストした結果は以下です。
全ブラウザで利用できる、画面外への表示を採用することとしました。
尚、今回はposition:fixed;left:100vwという記述を利用していますが、対象ブラウザの範囲を広げる場合は別途記述を検討して下さい。
環境 | テスト結果 | ||||
端末・OS | ブラウザ | バージョン | 1.display | 2.visibility | 3.画面外 |
iOS 11.4 | Safari | Webkit 605.1.15 | × | × | ○ |
Chrome | 67.0.3396.69 | × | × | ○ | |
Android 6.0 | Chrome | 66.0.3359.158 | × | × | ○ |
Web View | 66.0.3359.158 | × | × | ○ | |
Windows | Edge | 42.17134.1.0 | × | × | ○ |
IE11 | 11.112.17134.0 | × | × | ○ | |
Chrome | 67.0.3396.87 | × | × | ○ | |
Firefox | 60.0.2 | × | ○ | ○ | |
Mac OSX | Safari | 11.1 | × | × | ○ |
コピーをするのに、テキストエリアをフォーカスして中身を選択しますが、その際にスマホ特有の動作が発生します。これを避ける方法を探りました。
iOSでは、フォーム内の文字が小さい場合はフォームを自動で拡大します。
拡大の判定基準が16pxなので、textareaにfont-size:16pxのstyleを設定しました。
次に、iOS・Android共通でソフトキーボードが起動します。
あれこれ試した結果、readonly=readonlyの属性を設置することとしました。念の為、コピー機能自体に支障がないかをPCも含めて調査しています。
以下のテスト結果から、textareaにfont-size:16pxのstyleとreadonly属性を設置することでいずれも解決可能です。
環境 | テスト結果 | |||
端末・OS | ブラウザ | バージョン | focus時の拡大 font-size:16px | ソフトキーボード起動 readonly |
iOS 11.4 | Safari | Webkit 605.1.15 | ○ | ○ |
Chrome | 67.0.3396.69 | ○ | ○ | |
Android 6.0 | Chrome | 66.0.3359.158 | -- | ○ |
Web View | 66.0.3359.158 | -- | ○ | |
Windows | Edge | 42.17134.1.0 | -- | ○動作問題なし |
IE11 | 11.112.17134.0 | -- | ○動作問題なし | |
Chrome | 67.0.3396.87 | -- | ○動作問題なし | |
Firefox | 60.0.2 | -- | ○動作問題なし | |
Mac OSX | Safari | 11.1 | -- | ○動作問題なし |
以上から、当サイトが推奨するクロスブラウザなコピー機能の関数は以下です。
jQueryで実装しています。非jQueryの場合は一部を差し替えて下さい。
function copy(str) {
if(!str || typeof(str) != "string") {
return "";
}
//strを含んだtextareaをbodyタグの末尾に設置
$(document.body).append("<textarea id=\"tmp_copy\" style=\"position:fixed;right:100vw;font-size:16px;\" readonly=\"readonly\">" + str + "</textarea>");
//elmはtextareaノード
var elm = $("#tmp_copy")[0];
//select()でtextarea内の文字を選択
elm.select();
//rangeでtextarea内の文字を選択
var range = document.createRange();
range.selectNodeContents(elm);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
elm.setSelectionRange(0, 999999);
//execCommandを実施
document.execCommand("copy");
//textareaを削除
$(elm).remove();
}
上記の短いプログラムは自己責任でご自由にご利用下さい。
問題点やお気づきの点などありましたら、下記にあるメールアドレスまでご連絡願います。