
ブラウザのポップアップがブロックされる仕組みであるUser Activationの挙動を調べてみた
西田@リテールアプリ共創部マッハチームです。
今回は Chrome で window.open() を使うとポップアップブロックされたので、ブロックされる条件や仕組みを調べ、検証してみました。
ポップアップがブロックされる仕組み
ブラウザがどのようにユーザー操作を追跡し、なぜ特定の操作をブロックするのかを説明します。
ブロックされる操作
以下の操作はユーザー操作の直後でなければブロックされる可能性があります。これらは一部の例であり、完全なリストはMDN: User activationを参照してください。
- ポップアップウィンドウの表示(
window.open()) - フルスクリーンモードへの移行(
Element.requestFullscreen()) - クリップボードへのアクセス(
Clipboard.read()/Clipboard.write()) - ファイルピッカーの表示(
showOpenFilePicker()/showSaveFilePicker()) - 画面共有の開始(
MediaDevices.getDisplayMedia()) - ピクチャインピクチャの表示(
HTMLVideoElement.requestPictureInPicture()) - Web Share APIの呼び出し(
Navigator.share()) - 決済UIの表示(
PaymentRequest.show()) - デバイスアクセス(
Serial.requestPort()、USB.requestDevice()、HID.requestDevice()など) - XR関連(
XRSystem.requestSession())
ブロックされる条件
ページ遷移後にユーザーがブラウザで何も操作していない状態で、ブロックされる操作を JavaScript で実行しようとするとブロックされます。例えば、リダイレクト処理を window.location.href の書き換えでなく、 window.open() を使って、別ウィンドウで行おうとした場合などに発生します
また、 ユーザーがブラウザ上で操作した と判定される状態は、ブロックされる操作を行うか、一定時間後に消費され、無効化されます。

一定時間経過後操作がブロックされる例としては以下のようなケースがあります。
setTimeoutやsetIntervalのコールバック内で、ユーザー操作から一定時間経過後に実行された場合fetchやXMLHttpRequestの非同期コールバック内で実行された場合(レスポンス待機中に時間が経過するため)
逆に、以下の場合はブロックされません。
click、keydown、touchendなどのユーザー操作イベントハンドラ内で直接実行された場合- ユーザー操作から数秒以内(ブラウザにより異なる)に実行された場合
注意点としてはelement.click()などスクリプトから発火させたイベントでは、ユーザーがブラウザを操作した状態にならず、操作がブロックされます
ブロックされる理由
これらの操作がブロックされる主な理由は、ユーザーの意図しない動作を防ぐためです。
ポップアップウィンドウは、かつて広告や詐欺サイトへの誘導に悪用されてきた歴史があります。ユーザーの意図しない大量のウィンドウ表示やクリックジャッキングなどの問題が報告されたため、ブラウザベンダーは対策を講じるようになりました。
同様に、フルスクリーンモードへの強制遷移はフィッシング詐欺に、クリップボードへの無断アクセスは個人情報の窃取に悪用される恐れがあります。決済UIやデバイスアクセスについては、ユーザーの明示的な意図なく実行されると金銭的被害やプライバシー侵害につながります。
そのため、現代のブラウザはこれらの操作を「ボタンのクリックやキー入力など、ユーザーが明示的に操作した直後」に限定することで、悪意のあるスクリプトによる濫用を防いでいます。
参考:
ブロックされる仕組み
ブラウザは User Activation という仕組みでユーザー操作を追跡しています。User Activationには、Transient Activation(一時的アクティベーション)と Sticky Activation(粘着的アクティベーション)の2つの状態があります。
Transient Activation(一時的アクティベーション)
ユーザーがクリックやキー入力などの操作を行うと、ブラウザは一時的に「アクティブ」状態になります。この状態は数秒で自動的に失効するか、window.open()などの特定のAPIを呼び出すと消費されます。window.open()がブロックされるかどうかは、このTransient Activationが有効かどうかで決まります。
Sticky Activation(粘着的アクティベーション)
ユーザーが一度でもページと対話すると有効になり、以降はページを離れるまでリセットされません。Navigator.vibrate()やメディアの自動再生などはこちらの状態を参照します。
User Activationをトリガーするイベント
以下のイベントがUser Activationをトリガーします(isTrusted属性がtrueの場合のみ)。
keydown(Escキーやブラウザ予約キー等を除く)mousedownpointerdown(pointerTypeが"mouse"の場合)pointerup(pointerTypeが"mouse"以外の場合)touchend
これらはユーザー自身の操作によって発火したイベントに限られます。element.click()のようにスクリプトから発火させたイベントではUser Activationはトリガーされません。
JavaScriptでの状態確認
// Transient Activation の確認
navigator.userActivation.isActive
// Sticky Activation の確認
navigator.userActivation.hasBeenActive
検証
実際にChromeとSafariでUser Activationの動作を検証してみました
検証環境
| 項目 | バージョン |
|---|---|
| Chrome | 143.0.7499.194 |
| Safari | 18.6 (20621.3.11.11.3) |
| 検証日 | 2026-01-23 |
検証結果サマリ
最初に検証結果のサマリから
| 狙い | 方法 | Chrome | Safari |
|---|---|---|---|
| User Activationなしでの動作確認 | ページ読み込み直後にwindow.open()実行 |
ブロック | ブロック |
| クリック直後の動作確認 | clickイベント内で即座にwindow.open()実行 |
成功 | 成功 |
| 非同期処理後の動作確認 | clickイベント内でfetch後にwindow.open()実行 |
成功 | 成功 |
| タイムアウト時間の検証 | clickイベント内でsetTimeout(1秒)後に実行 | 成功 | ブロック |
| タイムアウト時間の検証 | clickイベント内でsetTimeout(5秒)後に実行 | ブロック | ブロック |
| 消費の検証 | clickイベント内でwindow.open()を複数回実行 |
1回目のみ成功 | 1回目のみ成功 |
検証結果のまとめ
ブラウザ別Transient Activationタイムアウト
| ブラウザ | タイムアウト時間 |
|---|---|
| Chrome | 約5秒 |
| Safari | 約1秒 |
実務上の注意点
- Safariは特に厳格: Safariのタイムアウトが約1秒とChromeの約1/5であるため、クロスブラウザ対応ではSafariの制約を基準に設計する必要があります。
- 複数ポップアップは不可: 1回のクリックで複数のポップアップを開くことはできません(Chrome/Safari共通)。各ポップアップに対して個別のユーザー操作が必要です。
- 非同期処理との組み合わせに注意: API呼び出しなどの非同期処理後に
window.open()を呼び出す設計は、レスポンス時間によってはブロックされる可能性があります。
対処法
非同期処理後に別ウィンドウを開きたい場合は、以下のアプローチを検討してください。
- 同じウィンドウで遷移する:
window.location.hrefを使用すれば、User Activationの制約を受けません。 - モーダルやインラインで表示する: 別ウィンドウを開く代わりに、同一ページ内で結果を表示します。
- 処理完了後に再度ボタンを押してもらう: 非同期処理の完了後に「結果を開く」ボタンを表示し、ユーザーに再度クリックしてもらいます。
これらの方法は、User Activationの設計意図(ユーザーの明示的な意図を確認する)に沿った正攻法です。今後ブロックされる確率は低いと考えられます
各検証の詳細
テスト1: ページ読み込み直後(ユーザー操作なし)
ユーザーが何も操作していない状態でwindow.open()を実行した場合です。
// ページ読み込み時に即座に実行
(function() {
const popup = window.open('about:blank', '_blank');
if (popup) {
console.log('成功');
} else {
console.log('ブロック');
}
})();
| ブラウザ | 結果 | navigator.userActivationの状態 |
|---|---|---|
| Chrome | ブロック | isActive: false, hasBeenActive: false |
| Safari | ブロック | isActive: false, hasBeenActive: false |
ページ読み込み直後はUser Activationが存在しないため、window.open()はブロックされます。
テスト2: クリック直後のwindow.open()
ボタンクリックのイベントハンドラ内で直接window.open()を呼び出した場合です。
button.addEventListener('click', () => {
window.open('about:blank', '_blank');
});
| ブラウザ | 結果 |
|---|---|
| Chrome | 成功 |
| Safari | 成功 |
テスト3: fetch()完了後のwindow.open()
fetch()による非同期処理の完了後にwindow.open()を呼び出した場合です。
button.addEventListener('click', async () => {
await fetch('https://example.com/api');
window.open('about:blank', '_blank');
});
| ブラウザ | 結果 |
|---|---|
| Chrome | 成功 |
| Safari | 成功 |
fetchのレスポンスが十分に高速であれば、Transient Activationのタイムアウト内に収まるため成功します。
テスト4: setTimeout後のwindow.open()
クリック後、一定時間経過してからwindow.open()を呼び出した場合です。Transient Activationのタイムアウト時間を検証しました。
button.addEventListener('click', () => {
setTimeout(() => {
window.open('about:blank', '_blank');
}, delay);
});
| 遅延時間 | Chrome | Safari |
|---|---|---|
| 0ms | 成功 | 成功 |
| 500ms | 成功 | 成功 |
| 1秒 | 成功 | ブロック |
| 2秒 | 成功 | ブロック |
| 3秒 | 成功 | ブロック |
| 5秒 | ブロック | ブロック |
| 10秒 | ブロック | ブロック |
Safariの方がChromeよりタイムアウトが短い
- Chrome: Transient Activationのタイムアウトは約5秒
- Safari: Transient Activationのタイムアウトは約1秒(500ms〜1秒の間)
- Safariは Chromeより大幅に短いタイムアウトを採用しています
テスト5: 複数回のwindow.open()
1回のクリックで複数回window.open()を呼び出した場合です。
button.addEventListener('click', () => {
window.open('about:blank', '_blank'); // 1回目
window.open('about:blank', '_blank'); // 2回目
window.open('about:blank', '_blank'); // 3回目
});
| 回数 | Chrome | Safari |
|---|---|---|
| 1回目 | 成功 | 成功 |
| 2回目 | ブロック | ブロック |
| 3回目 | ブロック | ブロック |
Transient Activationは1回のwindow.open()で「消費」されるため、2回目以降はブロックされます。複数のポップアップを開きたい場合は、それぞれに対してユーザー操作が必要です。
最後に
User Activationは、ユーザーの意図しない操作を防ぐための重要な仕組みです。特にSafariのタイムアウトが約1秒と短いことは、クロスブラウザ対応の際に意識しておくべきポイントです。
非同期処理後にwindow.open()を使いたくなる場面は実務でもよくありますが、そもそも別ウィンドウで開く必要があるのかを再検討し、window.location.hrefでの遷移やモーダル表示など、User Activationの制約を受けない方法を選択するのが堅実です。
この記事が誰かの役に立てば幸いです。
参考
- MDN: User activation
- Chrome for Developers: Making user activation consistent across APIs
- WebKit Blog: The User Activation API
- WHATWG HTML Standard: Interaction
- GitHub Issue: Investigate how to avoid Safari's popup-blocker
エンジニア募集のお知らせ
リテールアプリ共創部マッハチームではエンジニアを大募集しています!
マッハチームでは 0→1 の新規案件の立ち上げを専門に、AI駆動で高速にプロダクトを開発しています。
AI駆動開発がしたい方、モダン技術を用いてフロントエンドもサーバーサイドもインフラもTypeScriptでフルスタックに開発したい方、プリセール・顧客折衝も要件定義も開発も全部やりたい方は、是非以下のブログも読んでみてください。
お気軽にカジュアル面談もお待ちしています!








