
コードブロックの「コピー」ボタンをClipboard APIで実装したら、本番では動くのに検証環境では反応しない——原因はバグではなく、Clipboard APIがhttps(セキュアコンテキスト)でしか使えない仕様です。この記事では、当社ブログのCOPYボタン実装で実際にこの制約を踏んだ経緯と、http環境でも動くフォールバック付きの実装を紹介します。
背景: ブログにコードのCOPYボタンを付けた
当社のTECH-BLOGでは、コードブロックにマウスを乗せるとCOPYボタンが現れ、クリックでコード全文をクリップボードへコピーできるようにしています。実装はシンプルで、記事内のpre要素をラップしてボタンを差し込むだけです。
document.querySelectorAll('.article pre').forEach(function(pre){
var wrap = document.createElement('div'); wrap.className = 'code-wrap';
pre.parentNode.insertBefore(wrap, pre); wrap.appendChild(pre);
var btn = document.createElement('button'); btn.className = 'code-copy'; btn.type = 'button'; btn.textContent = 'COPY';
btn.addEventListener('click', function(){
ndCopy(pre.querySelector('code').innerText).then(function(){
btn.textContent = 'COPIED!'; btn.classList.add('done');
setTimeout(function(){ btn.textContent = 'COPY'; btn.classList.remove('done'); }, 1600);
});
});
wrap.appendChild(btn);
});
コピー処理の本体は、最初はnavigator.clipboard.writeText()を呼ぶだけの1行でした。
ハマりポイント: Clipboard APIはhttpsでしか存在しない
当社サイトの構築中、検証はIP制限をかけたステージング環境で行っていました。SSL証明書は設定済みでしたが、WordPressのURL設定(siteurl)をhttpsへ切り替える前だったため、サイト内は実質httpで動いている状態です。そこでCOPYボタンをクリックしても、何も起きませんでした。
原因はClipboard APIの仕様です。navigator.clipboardはセキュアコンテキスト(https、またはlocalhost)でのみ提供されます(MDN: クリップボード API / 保護されたコンテキスト)。httpのページではプロパティ自体がundefinedになるため、「エラーが出る」のではなく「静かに何も起きない」のが厄介なところです。開発をlocalhostで行っていると気づけず、httpの検証サーバーや社内環境に載せた瞬間に発覚する、という踏み方をします。
解決: セキュアコンテキストを判定してフォールバック
httpsではClipboard APIを使い、httpでは旧来のdocument.execCommand('copy')に切り替える関数を用意しました。どちらもPromiseを返すので、呼び出し側は同じ書き方のままです。
// クリップボード書き込み(https=Clipboard API / httpステージング=execCommandフォールバック)
function ndCopy(text){
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
return new Promise(function(res, rej){
var ta = document.createElement('textarea'); ta.value = text;
ta.style.position = 'fixed'; ta.style.opacity = '0';
document.body.appendChild(ta); ta.select();
try { document.execCommand('copy') ? res() : rej(); } catch (e) { rej(e); }
document.body.removeChild(ta);
});
}
要点は3つです。
- 判定は2条件:
navigator.clipboardの存在に加えてwindow.isSecureContextも見ます。環境によってはプロパティがあっても書き込みが拒否されるケースへの保険です。 - フォールバックは不可視のtextarea: 値を入れたtextareaを画面外に置いてselect()し、execCommand(‘copy’)で選択範囲をコピーします。position:fixed+opacity:0にするのは、挿入時にページがスクロールしたりチラついたりするのを防ぐためです。
- execCommandは非推奨(MDN)だが、フォールバック用途なら現役: 新規実装の主役にすべきではありませんが、「httpsでない環境でも動かしたい」場面では今も実用的な逃げ道です。
同じ関数を「記事リンクをコピー」ボタンでも使い回しています。コピー先の文字列が変わるだけで、呼び出し方は同一です。
検証
httpのステージング環境で、COPYボタン(→COPIED!表示)と「リンクをコピー」(→コピーしました表示)の両方を実際にクリックし、クリップボードに正しい内容が入ることを確認しました。本番のhttps環境ではClipboard API側のパスが使われるため、フォールバックの品質に依存しない構成です。
まとめ
Clipboard APIが「httpでは静かに動かない」ことを知っていれば、原因調査は数分で済みます。isSecureContextで分岐してexecCommandへフォールバックする10行少々を関数にしておけば、検証環境から本番まで同じコードで通せます。
Webサイトの構築や、こうした細部まで動くUIの実装のご相談は、SERVICESからお気軽にどうぞ。
← TECH-BLOG一覧へ