Chrome拡張でスクリーンショットを「クリック前」に撮る方法|mousedownとcaptureVisibleTabの組み合わせ

スポンサーリンク

ユーザーと一緒にブラウザ操作録画ツール(HowTo Recorder)を作っていたときに引っかかった話です。「clickイベントで撮影しているのに、スクリーンショットが次のページの読み込み画面になってしまう」という問題が起きました。原因と解決策をまとめます。

最初の設計:clickの後にcaptureVisibleTab

最初の実装は「clickイベントを検知 → 300ms待機 → captureVisibleTab」という設計でした。「ページが更新されてから撮影したほうが状態が安定する」という考えでしたが、これが完全に逆でした。

ハウツー記録ツールで必要なのは「このボタンをクリックします」という操作の説明です。つまりスクリーンショットにはクリック対象の要素が見えている画面が必要です。clickの後に撮影すると、すでにページ遷移が始まっており「読み込み中」の画面や次のページが写ってしまいます。

ハマったポイント

症状:ボタンをクリックしてもスクリーンショットに次のページが写る。遅延を短くしても変わらない。

原因:clickイベントはナビゲーションのトリガーになる。content.jsがsendMessageを呼んでからbackground.jsがcaptureVisibleTabを実行するまでの数十msで、すでにページ遷移が始まっている。

解決策:mousedownイベントで即座にスクリーンショットを撮影して一時保存し、clickイベントで要素の説明文と合体させる2ステップ方式にする。

// content.js
document.addEventListener('mousedown', function(e) {
  if (!isRecording) return;
  chrome.runtime.sendMessage({ type: 'CAPTURE_NOW' });
}, true);

document.addEventListener('click', function(e) {
  if (!isRecording) return;
  chrome.runtime.sendMessage({
    type: 'STEP_EVENT',
    description: getDescription(e.target)
  });
}, true);
// background.js
let pendingScreenshot = null;

// CAPTURE_NOW: mousedown時点で即座に撮影
chrome.tabs.captureVisibleTab(null, { format: 'png' }, (dataUrl) => {
  pendingScreenshot = dataUrl;
});

// STEP_EVENT: 保存済みスクリーンショットを使用
async function handleStepEvent(message) {
  const screenshot = pendingScreenshot || await captureNow();
  pendingScreenshot = null;
  // stepsに追記...
}

なぜmousedownが有効なのか

ブラウザのイベント順序は mousedown → mouseup → click です。mousedownはボタンを押し込んだ瞬間に発火し、まだユーザーが指を離していないため、ページ遷移も始まっていません。ここでスクリーンショットを撮れば確実に「操作前の画面」が取得できます。

補足として、Chrome拡張のポップアップは別ウィンドウとして描画されるため、ポップアップが開いた状態でcaptureVisibleTabを呼んでもポップアップは写り込みません。タブのコンテンツのみが撮影されます。

まとめ

Chrome拡張でクリック前の画面を撮影したい場合は、clickではなくmousedownをトリガーにします。「mousedownで即撮影→pendingScreenshotに保持→clickで説明文と合体」という2ステップパターンが、ページ遷移を伴う操作の記録に有効です。HowTo記録ツール・操作ログ・E2Eテストのスクリーンショット取得など、同様の要件がある場面で応用できます。

コメント

タイトルとURLをコピーしました