【JavaScript】ポップアップウィンドウを開く方法|window.open・サイズ位置指定・ポップアップブロック対策・postMessage 親子間通信まで解説

JavaScriptの window.open() を使うと、別のブラウザウィンドウ(またはタブ)をプログラムから開くことができます。ヘルプページ・利用規約・OAuth認証画面など、現在のページを離れずに別コンテンツを表示したい場面で活用されます。

ただし、現代のブラウザはポップアップブロッカーを搭載しており、使い方を誤るとブロックされてしまいます。この記事では基本的な使い方から、ブロック対策・ウィンドウ間通信・モーダルとの使い分けまで体系的に解説します。

スポンサーリンク

window.open() の基本構文

window.open(url, target, features) の3つの引数で挙動を制御します。

window.open の基本
// 基本形
window.open('https://example.com');

// 第2引数:ウィンドウ名(_blank=新規, _self=現タブ, _parent, _top, または任意の名前)
window.open('https://example.com', '_blank');

// 第3引数:ウィンドウの外観を指定する文字列(スペースなし・カンマ区切り)
window.open(
  'https://example.com',
  'myPopup',
  'width=800,height=600,left=100,top=100'
);

// 戻り値:開いたウィンドウへの参照(ブロックされた場合は null)
const popup = window.open('https://example.com', '_blank');
if (popup === null) {
  console.log('ポップアップがブロックされました');
}
引数 内容 省略時の動作
url 開く URL(空文字列 "" で空白ページ) 空白ページ(about:blank)
target ウィンドウ名または _blank_self _blank(新規)
features サイズ・位置などの設定文字列 ブラウザのデフォルト

ウィンドウのサイズ・位置を指定する(features)

第3引数の features 文字列で、ウィンドウのサイズと表示位置を制御できます。

オプション 説明
width ウィンドウの幅(px) width=800
height ウィンドウの高さ(px) height=600
left 画面左端からの距離(px) left=200
top 画面上端からの距離(px) top=100
noopener opener プロパティを無効化(セキュリティ強化) noopener
noreferrer Referer ヘッダーを送らない noreferrer
画面中央に表示するポップアップ
/**
 * 画面中央にポップアップを表示する
 * @param {string} url
 * @param {number} w - 幅(px)
 * @param {number} h - 高さ(px)
 */
function openCentered(url, w = 800, h = 600) {
  const left = Math.round(window.screenX + (window.outerWidth  - w) / 2);
  const top  = Math.round(window.screenY + (window.outerHeight - h) / 2);

  return window.open(
    url,
    '_blank',
    `width=${w},height=${h},left=${left},top=${top},noopener,noreferrer`
  );
}

// 使い方
openCentered('https://example.com/help', 900, 650);
toolbar・scrollbars などは現代では無効:かつては toolbar=nomenubar=noscrollbars=yes などのオプションがありましたが、現代のブラウザはセキュリティ上の理由からこれらのほとんどを無視します。制御できるのは実質的に widthheightlefttop のみと考えてください。

ポップアップブロッカーの仕組みと対策

現代のブラウザは、ユーザー操作(クリックなど)を起点としない window.open() の呼び出しをブロックします。

呼び出しパターン ブロックされるか
クリックイベントハンドラ内で即時呼び出し ◎ ブロックされない
ページ読み込み時(DOMContentLoaded など)に自動呼び出し ✕ ブロックされる
クリック後に setTimeout で遅延呼び出し ✕ ブロックされる(タイミングによる)
クリック後に await(非同期)してから呼び出し ✕ ブロックされる
async/await 後の window.open はブロックされる:await fetch(...) の後に window.open() を呼ぶと、ブラウザはユーザー操作の「信頼コンテキスト」が切れたと判断してブロックします。非同期処理が必要な場合は、先に window.open("") で空ウィンドウを開いておき、後から popup.location.href = url で URL を設定するのが正しいパターンです。
OK: クリックで即時開く(ブロックされない)
document.getElementById('openBtn').addEventListener('click', () => {
  // クリックハンドラ内で即時呼び出し → ブロックされない
  window.open('https://example.com', '_blank', 'noopener,noreferrer');
});
OK: 非同期処理が必要なケースの対処法
document.getElementById('authBtn').addEventListener('click', async () => {
  // ① クリック直後に空ウィンドウを先に開いておく
  const popup = window.open('', '_blank', 'width=500,height=700');

  try {
    // ② 非同期で認証URLを取得
    const res = await fetch('/api/auth/url');
    const { authUrl } = await res.json();

    // ③ 取得したURLをポップアップに設定
    popup.location.href = authUrl;
  } catch (err) {
    // 失敗したらポップアップを閉じる
    popup.close();
    alert('認証URLの取得に失敗しました');
  }
});

ポップアップブロックの検出とフォールバック

ブロックされた場合 window.open()null を返します。これを検出して通常リンクへのフォールバックを用意すると、ユーザビリティが向上します。

ブロック検出とフォールバック
function openPopupWithFallback(url, options = 'width=800,height=600,noopener,noreferrer') {
  const popup = window.open(url, '_blank', options);

  if (popup === null || popup.closed) {
    // ブロックされた → 通常のタブで開くようユーザーに案内
    const openLink = document.createElement('a');
    openLink.href   = url;
    openLink.target = '_blank';
    openLink.rel    = 'noopener noreferrer';
    openLink.textContent = 'こちらをクリックして開いてください';

    const msg = document.getElementById('popupMsg');
    if (msg) {
      msg.innerHTML = '';
      msg.append('ポップアップがブロックされました。', openLink);
      msg.hidden = false;
    }
  }
  return popup;
}

開いたウィンドウを制御する(opener・closed・focus)

window.open() の戻り値はウィンドウへの参照です。これを使って、ポップアップの状態確認・フォーカス・クローズを制御できます。

開いたウィンドウの制御
let popup = null;

document.getElementById('openBtn').addEventListener('click', () => {
  // すでに開いていて閉じられていなければ再利用
  if (popup && !popup.closed) {
    popup.focus(); // 既存のウィンドウを前面に
    return;
  }

  popup = window.open(
    '/help',
    'helpWindow',
    'width=900,height=700,noopener'
  );
});

document.getElementById('closeBtn').addEventListener('click', () => {
  if (popup && !popup.closed) {
    popup.close();
    popup = null;
  }
});

// ポップアップが閉じられたかどうかを定期確認
const checkInterval = setInterval(() => {
  if (popup && popup.closed) {
    console.log('ポップアップが閉じられました');
    popup = null;
    clearInterval(checkInterval);
  }
}, 500);
noopener を使うと opener プロパティが無効になる:features に noopener を指定すると、開いた先のページから window.opener で元ページを参照できなくなります。これにより悪意あるページが window.opener.location を書き換えるタブナビゲーション攻撃(reverse tabnapping)を防げます。外部サイトを開く場合は必ず noopener,noreferrer を指定してください。

postMessage でポップアップと安全に通信する

同じオリジンであれば popup.document に直接アクセスできますが、より安全でオリジン間でも使えるのが postMessage です。OAuth 認証の結果を親ウィンドウに返すケースなどで活用されます。

親ウィンドウ側(メッセージを受け取る)
// 親ウィンドウ:postMessage のリスナーを設定
window.addEventListener('message', (e) => {
  // ① 送信元オリジンを必ず検証する(セキュリティ上必須)
  if (e.origin !== 'https://your-domain.com') return;

  // ② データを処理
  const { type, payload } = e.data;

  if (type === 'AUTH_SUCCESS') {
    console.log('認証成功:', payload.token);
    // ポップアップを閉じて処理を続ける
    if (popup && !popup.closed) popup.close();
  }
});
ポップアップ側(メッセージを送る)
// ポップアップウィンドウ側のスクリプト
// 認証成功後などに親ウィンドウへメッセージを送信
function notifyParent(token) {
  if (!window.opener || window.opener.closed) return;

  window.opener.postMessage(
    { type: 'AUTH_SUCCESS', payload: { token } },
    'https://your-domain.com' // ターゲットオリジンを明示(必須)
  );

  // 送信後に自分自身を閉じる
  window.close();
}
postMessage のオリジン検証は必須:message イベントのハンドラで e.origin を検証しないと、悪意あるサイトから偽のメッセージを送られるリスクがあります。必ず e.origin !== "https://your-domain.com" の検証を最初に行ってください。また送信側でもターゲットオリジンを "*"(ワイルドカード)にせず、具体的なオリジンを指定してください。

window.open とモーダルダイアログの使い分け

window.open(新規ウィンドウ) モーダルダイアログ
親ページの操作 可能(独立したウィンドウ) 不可(背景がブロックされる)
URL の変化 別ウィンドウに独立したURL 変化しない
ポップアップブロック ユーザー操作起点でないとブロック ブロックなし(DOM操作のため)
適した用途 外部サービス・OAuth・ヘルプページ 確認ダイアログ・画像拡大・フォーム
モバイル対応 タブとして開く(UXが分断されやすい) 同一ページ内でUXが完結
現代のUIはモーダルを優先:新規ウィンドウを開くとユーザーのコンテキストが分断されるため、利用規約の表示・確認ダイアログ・画像プレビューなどはモーダルダイアログが推奨されます。window.open() は外部サービス連携(OAuth・決済ページ)など、独立したセッションが必要なケースに限定して使うのが現代のベストプラクティスです。

よくある質問

Qwindow.open で開いたウィンドウのサイズをあとから変更できますか?
Apopup.resizeTo(幅, 高さ) または popup.resizeBy(差分幅, 差分高さ) で変更できます。ただし noopener を指定した場合は popup への参照が null になるため使えません。また、ブラウザによっては自分で開いたウィンドウ以外のリサイズを制限しています。
Qwindow.open で開いたウィンドウに JS でコンテンツを直接書き込めますか?
A同一オリジンであれば popup.document.write()popup.document.body.innerHTML で書き込めます。別オリジンの場合は Same-Origin Policy によりアクセスが制限されます。オリジン間でデータをやり取りするには postMessage を使ってください。
Qtarget=”_blank” の <a> タグと window.open の違いは何ですか?
A<a target="_blank"> はユーザーがクリックしたときに開くため、ポップアップブロッカーに引っかかりません。window.open() はプログラムから呼び出せる分、ポップアップブロッカーの制約を受けます。セキュリティ面では両方とも rel="noopener noreferrer"(aタグ)または noopener,noreferrer(window.open)を指定してください。
Qスマートフォンで window.open は動作しますか?
Aスマートフォンのブラウザでは新しいタブとして開かれます。独立したウィンドウサイズの指定は無視されます。またポップアップブロッカーはデスクトップと同様に動作するため、ユーザー操作起点で呼び出す必要があります。モバイルではウィンドウ分割の概念がないため、重要な情報はモーダルで表示する方がUX上優れています。
Qwindow.open で開いたウィンドウが閉じられたことをリアルタイムに検知したいです。
A開いたウィンドウへの参照の .closed プロパティを setInterval で定期的に確認するのが一般的です。ただしこれはポーリングのため100%リアルタイムではありません。より確実にするには、ポップアップ側で閉じる前に window.opener.postMessage で通知する設計が推奨されます。

まとめ

JavaScript のポップアップウィンドウ実装のポイントをまとめます。

  • window.open(url, target, features) で別ウィンドウ・タブを開ける
  • ポップアップブロッカーを回避するにはクリックイベントハンドラ内で即時呼び出す
  • 非同期処理後に開きたい場合は「空ウィンドウを先に開いて後から URL を設定」する
  • 外部URLを開く際は必ず noopener,noreferrer を指定してセキュリティを確保する
  • ウィンドウ間通信は postMessage + オリジン検証で安全に行う
  • モーダルで代替できる場面ではモーダルを優先する(UX・セキュリティ両面で有利)

ページ内のモーダルダイアログ実装は【JavaScript】Micromodal.jsで簡単にモーダルを実装する方法、既存要素の移動・制御については【JavaScript】既存の要素を移動させる方法もあわせてご覧ください。