window.open の使い方|別タブ・サイズ指定・ポップアップ対策

js-open-window JavaScript
記事内に広告が含まれています。

「JavaScriptで別タブを開きたい」という場面でよく使われるのがwindow.open()です。基本的な書き方はシンプルですが、実際に使ってみると「新しいタブが開かない」「SafariやChromeで挙動が違う」「fetchの処理後だとポップアップブロックされる」といった問題に悩むことがあります。

また、window.open()は単にURLを開くだけの機能ではありません。_blankなどのターゲット指定、ウィンドウサイズの設定、開いたウィンドウの再利用・クローズ、window.openerを介した親ウィンドウとの関係など、実装前に知っておきたい仕様があります。セキュリティ面では、noopenernoreferrer、reverse tabnabbingへの対策も重要です。

この記事では、window.open()の基本から実際の開発で遭遇しやすい問題まで、具体的なコードを交えながら解説します。

この記事を読んでわかること

  • window.open()の基本構文と第1〜第3引数の意味
  • 新しいタブ・別ウィンドウを開く具体的な方法
  • <a target="_blank">window.locationとの使い分け
  • ポップアップブロックが発生する原因と対処法
  • fetchsetTimeoutなどの非同期処理と組み合わせる際の注意点
  • window.close()closedプロパティを使ったウィンドウ操作
  • window.postMessage()による親子ウィンドウ間の安全な通信方法
  • noopenernoreferrerを使ったセキュリティ対策
  • PDF・画像・Blob URLなどを別タブで表示する実践例
  • TypeScriptやjQueryでwindow.open()を利用する方法
  1. window.open()とは?JavaScriptで別タブ・別ウィンドウを開く基本
    1. window.open()の役割と基本構文
    2. 第1引数〜第3引数(URL・ターゲット名・features)の正しい指定順と仕様
    3. aタグ(target=”_blank”)やwindow.locationとの使い分け基準とメリット
  2. ポップアップブロックの原因と回避する実装パターン
    1. なぜブロックされる?onclick等ユーザーアクション直下での必須実行ルール
    2. fetchやsetTimeoutなど非同期処理完了後に確実にwindow.openを発火させる裏技
    3. 戻り値(null/undefined)を利用したポップアップブロック発生の検知とエラー判定方法
  3. 開いた子ウィンドウの操作:再利用・閉じる処理・window.postMessage連携
    1. window.close()で画面を閉じる方法とclosedプロパティでの開閉状態チェック
    2. 同じウィンドウ名を指定して既存タブを新規立ち上げせずに再利用・フォーカスする技
    3. クロスドメイン環境で安全に親子通信を行うwindow.postMessageの実装方法
  4. セキュリティ対策:noopener・noreferrer の正しい指定方法
    1. タブジャッキング・reverse tabnabbing 攻撃の仕組みと危険性
    2. rel=”noopener noreferrer” を付与するHTMLとJavaScriptの両方の実装
    3. window.opener を使った親ウィンドウ操作のリスクと noopener 無効化の対策
  5. 実践的な活用シーン別の実装例
    1. PDF・画像・Blob URLを別タブでプレビュー表示する方法
    2. TypeScriptでwindow.open()を使う際のWindowProxy型エラー対処法
    3. jQuery環境でwindow.open()をイベントハンドラと組み合わせる書き方
    4. バナー広告制作におけるwindow.clicktagを使った遷移処理の実装
  6. よくある質問(FAQ)
  7. まとめ

window.open()とは?JavaScriptで別タブ・別ウィンドウを開く基本

window.open()は、JavaScriptから新しいタブやウィンドウを開くための標準メソッドです。URL・ターゲット名・ウィンドウの見た目を指定するだけで、任意のページを別コンテキストで表示できます。まずは基本構文と引数の仕様、そして似た機能である<a target="_blank">window.locationとの使い分けを押さえておきましょう。ここを正しく理解しておくことで、後述するポップアップブロックやセキュリティのトラブルも未然に防げるようになります。

window.open()とは?JavaScriptで別タブ・別ウィンドウを開く基本

window.open()の役割と基本構文

window.open()は、指定したURLを新しい「ブラウジングコンテキスト(タブ・ウィンドウ・フレーム)」に読み込むためのメソッドです。実務では「別タブでリンクを開きたいが、<a>タグではなくJavaScript側で動的に制御したい」という場面で使われることがほとんどです。

基本構文は以下の通りです。

// 基本構文:window.open(url, target, windowFeatures)
window.open();                                  // 何も指定しない場合、空のタブが開く
window.open("https://example.com");             // URLのみ指定
window.open("https://example.com", "_blank");   // 新しいタブで開く

window.open()を実行した瞬間にページがすぐ表示されると思われがちですが、実際にはURLの読み込みは非同期で行われ、window.open()が返した時点では新しいウィンドウの中身はabout:blank(空白ページ)のままです。その後、現在実行中のスクリプトが終了してから実際のURL読み込みが開始されます。この挙動を知らないと、「開いた直後に新しいウィンドウの中身を操作しようとしたら何も反映されない」という不具合の原因になるため、最初に押さえておくべきポイントです。

第1引数〜第3引数(URL・ターゲット名・features)の正しい指定順と仕様

window.open()の引数は「URL→ターゲット名→windowFeatures」の順で固定されており、途中を省略することはできても順番を入れ替えることはできません。それぞれの仕様は以下の通りです。

引数名称必須説明
第1引数url省略可読み込むリソースのURL。省略または空文字の場合は空白ページが開く
第2引数target省略可開き先のウィンドウ名。_blank_self_parent_topという特殊キーワードも指定可能
第3引数windowFeatures省略可width=400,height=300のように,区切り・name=value形式で見た目を指定
// 第1引数:開くURL
// 第2引数:ターゲット名(_blankで常に新しいタブ/ウィンドウ)
// 第3引数:ウィンドウの見た目(幅・高さなど)を指定
const newWin = window.open(
  "https://example.com",
  "_blank",
  "width=600,height=400"
);

// window.open()は新しいウィンドウへの参照(WindowProxyオブジェクト)を返す
// ポップアップブロック等で開けなかった場合はnullが返る点に注意
if (newWin === null) {
  console.log("ウィンドウを開けませんでした(ポップアップブロックの可能性)");
}

実務でよくある間違いが、「第2引数にwindowFeaturesの値を入れてしまう」というケースです。第2引数はあくまで「ウィンドウ名(target)」であり、width=600のような文字列を渡してもウィンドウのサイズには反映されません。サイズや位置を指定したい場合は、必ず第3引数に渡す必要があります。

aタグ(target=”_blank”)やwindow.locationとの使い分け基準とメリット

結論から言うと、静的なリンクであれば<a target="_blank">、JavaScriptによる動的な制御が必要な場合のみwindow.open()を使うのが基本方針です。同じタブ内でページを遷移させたいだけならwindow.locationを使います。3つの違いを整理すると以下の通りです。

方法新しいタブ/ウィンドウJSからの動的制御主な用途
<a href="..." target="_blank">△(クリックイベントが必要)静的なリンク(外部サイトへの遷移など)
window.open()条件分岐後に開く、サイズ指定、ポップアップ制御
window.location.href = "..."×(同一タブ)フォーム送信後のリダイレクトなど同一タブでの遷移
<!-- 静的なリンクなら、HTMLだけで完結する<a>タグがシンプルで推奨されます -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  外部サイトを新しいタブで開く
</a>
// 条件によって開くURLを変えたい場合など、動的な制御が必要な場面ではwindow.open()を使う
function openExternalLink(userType) {
  const url = userType === "admin"
    ? "https://example.com/admin"
    : "https://example.com/user";
  window.open(url, "_blank", "noopener,noreferrer");
}

案件で「会員種別によってリンク先を変えつつ、必ず別タブで開いてほしい」という要件が来たときは、<a>タグだけでは対応できないためwindow.open()を使うことになります。逆に、単に外部サイトへのリンクを設置するだけであれば、<a target="_blank">の方がSEOやアクセシビリティの観点からもシンプルで、余計なJavaScriptを書かずに済みます。「なぜwindow.open()が必要なのか」を実装前に一度立ち止まって考えることで、無駄なコードを減らすことができます。

Window: open() メソッド - Web API | MDN
open() は Window インターフェイスのメソッドで、指定されたリソースを、新しい、または既存の指定された名前を持った閲覧コンテキスト (ウィンドウ、 iframe、タブ) に読み込みます。
新世代レンタルサーバー『シンレンタルサーバー』

ポップアップブロックの原因と回避する実装パターン

window.open()が動かない原因の多くは「ポップアップブロック」です。ブラウザは、ユーザーの意図しないタイミングで新しいウィンドウが開くことを防ぐため、一定のルールに従ってブロック処理を行っています。結論として、ユーザーのクリックなどの操作イベントの中で、かつ同期的にwindow.open()を呼び出すことがブロックを回避する最も確実な方法です。非同期処理を挟むとブロックされやすくなる仕組みと、実際に開けたかどうかを判定する方法まで解説します。

ポップアップブロックの原因と回避する実装パターン

なぜブロックされる?onclick等ユーザーアクション直下での必須実行ルール

現代のブラウザは、ユーザーの操作(クリックやタップ)に直接紐づいて実行されたコードだけを「ユーザーの意図した操作」とみなし、それ以外のタイミングで発生したwindow.open()をブロックする仕組みを持っています。これは、ページを開いた瞬間に大量の広告ポップアップが表示されるような迷惑行為を防ぐための仕様です。

// 良い例:onclickイベントハンドラの中で、同期的にwindow.open()を呼び出す
button.addEventListener("click", () => {
  window.open("https://example.com", "_blank");
});
// 悪い例:ページ読み込み時に自動で実行される(ユーザー操作を伴わない)
window.addEventListener("load", () => {
  // これはほぼ全てのブラウザでブロックされる
  window.open("https://example.com", "_blank");
});

筆者が現場で最初に混乱したのは、「同じwindow.open()のコードなのに、書く場所によって動いたり動かなかったりする」という現象でした。原因はコードの中身ではなく、実行される「タイミング」がユーザー操作から離れているかどうかにありました。実装時は、必ず「このコードはクリックイベントの中で、遅延なく実行されているか」を意識するようにしてください。

fetchやsetTimeoutなど非同期処理完了後に確実にwindow.openを発火させる裏技

onclickの中に書いていても、fetchsetTimeoutなどの非同期処理を挟んでその完了後(コールバックやthenの中、awaitの後)にwindow.open()を呼び出すと、多くのブラウザでブロック対象になります。これは、非同期処理の完了タイミングが「ユーザー操作から時間的に離れてしまう」ため、ブラウザ側がユーザー操作起因のイベントと判断できなくなるためです。

// 悪い例:fetch完了後にwindow.openを呼ぶと、ブロックされる可能性が高い
button.addEventListener("click", async () => {
  const res = await fetch("/api/redirect-url");
  const data = await res.json();
  window.open(data.url, "_blank"); // ← awaitの後なのでブロックされやすい
});

回避策としてよく使われるのが、「先に空のウィンドウを同期的に開いておき、非同期処理が終わったタイミングでlocationを書き換える」という実装パターンです。

// 良い例:クリック直後に同期的に空のウィンドウを開いておく
button.addEventListener("click", async () => {
  // クリックイベント内で同期的にwindow.open()を呼ぶことで、ブロックを回避する
  const newWin = window.open("", "_blank");

  try {
    const res = await fetch("/api/redirect-url");
    const data = await res.json();
    // 非同期処理の完了後、開いておいたウィンドウのlocationを書き換える
    if (newWin) {
      newWin.location.href = data.url;
    }
  } catch (error) {
    // 通信に失敗した場合は、開いておいたウィンドウを閉じておく
    if (newWin) newWin.close();
    console.error("リダイレクト先の取得に失敗しました", error);
  }
});

この「先に空ウィンドウを開いておく」パターンは、API通信を挟んで遷移先を決定するような実務のケースで非常によく使われるため、ぜひ覚えておいてください。

戻り値(null/undefined)を利用したポップアップブロック発生の検知とエラー判定方法

window.open()は、ブラウザがブロックした場合や新しいウィンドウの生成に失敗した場合、戻り値としてnullを返します。この戻り値をチェックすることで、「ユーザーにポップアップブロックの解除を促す」といったフォールバック処理を実装できます。

// 戻り値をチェックしてポップアップブロックを検知する
const newWin = window.open("https://example.com", "_blank");

if (newWin === null || typeof newWin === "undefined") {
  // ブロックされた場合の処理(ユーザーへの案内など)
  alert("ポップアップがブロックされました。ブラウザの設定をご確認ください。");
} else {
  // 正常に開けた場合はフォーカスを移しておくと親切
  newWin.focus();
}

なお、Cross-Origin-Opener-Policy(COOP)ヘッダーが設定されているページでは、新しいウィンドウが別のブラウジングコンテキストグループで開かれ、参照が切り離されてclosedプロパティがtrueになるケースもあります。単純なnull判定だけでなく、こうしたセキュリティヘッダー起因の挙動があることも頭の片隅に置いておくと、原因不明のトラブルに直面した際に落ち着いて調査できます。

ブラウザごとの挙動差異

Chromeは比較的緩やかにユーザー操作からの遅延を許容する一方、Safari(特にiOS Safari)はポップアップブロックの判定が厳しく、awaitを一つ挟んだだけでもブロックされることがあります。Android Chromeはデスクトップ版Chromeに近い挙動をしますが、端末やOSのバージョンによって差が出ることもあるため、本番環境ではiOS Safari実機での動作確認を必ず行うことをおすすめします。Firefoxもユーザー操作起因かどうかを厳格にチェックする傾向があり、ブラウザ間で「動いたり動かなかったり」する代表的な原因の一つです。

国内シェアNo.1のエックスサーバーが提供するVPSサーバー『XServer VPS』

開いた子ウィンドウの操作:再利用・閉じる処理・window.postMessage連携

window.open()で開いたウィンドウ(子ウィンドウ)は、開きっぱなしにするだけでなく、閉じる・再利用する・親ウィンドウとデータをやり取りするといった制御が実務では頻繁に求められます。結論として、子ウィンドウの制御は「戻り値のWindowProxyオブジェクトを変数に保持しておく」ことがすべての基本になります。この参照さえ持っておけば、閉じる・状態確認・通信のいずれも実現できます。

開いた子ウィンドウの操作:再利用・閉じる処理・window.postMessage連携

window.close()で画面を閉じる方法とclosedプロパティでの開閉状態チェック

子ウィンドウを閉じたい場合は、window.open()の戻り値に対してclose()メソッドを呼び出します。また、そのウィンドウが既に閉じられているかどうかはclosedプロパティで判定できます。

// 子ウィンドウを開く際に、戻り値を変数に保持しておく
let childWindow = window.open("https://example.com", "_blank");

// 任意のタイミングで子ウィンドウを閉じる
function closeChildWindow() {
  if (childWindow && !childWindow.closed) {
    childWindow.close();
  }
}

// 子ウィンドウが閉じられているかどうかをチェックする
function checkChildWindowStatus() {
  if (childWindow === null) {
    console.log("そもそもウィンドウを開けていません");
  } else if (childWindow.closed) {
    console.log("子ウィンドウは既に閉じられています");
  } else {
    console.log("子ウィンドウは開いたままです");
  }
}

実務で起こりやすい失敗が、「子ウィンドウの参照をローカル変数として関数内に閉じ込めてしまい、別の関数からclose()を呼べなくなる」というケースです。子ウィンドウを後から操作する必要がある場合は、必ずモジュールスコープやコンポーネントのstateなど、複数の処理から参照できる場所にWindowProxyを保持しておくようにしましょう。なお、ユーザーが子ウィンドウのタブを手動で閉じた場合でもclosedプロパティは自動的にtrueに更新されるため、定期的にポーリングして状態を監視するといった実装も可能です。

同じウィンドウ名を指定して既存タブを新規立ち上げせずに再利用・フォーカスする技

window.open()の第2引数(ターゲット名)に同じ文字列を指定すると、既にそのウィンドウ名で開かれているタブ・ウィンドウが存在する場合は新規作成されず、既存のものが再利用されます。これを利用すると「同じボタンを何度クリックしても、タブが増殖しない」実装が可能になります。

// 同じウィンドウ名("previewWindow")を指定することで、既存のタブを再利用する
function openPreview(url) {
  // 2回目以降のクリックでは、新しいタブが増えずに既存タブのURLが更新される
  const win = window.open(url, "previewWindow");

  if (win) {
    // 既存タブが背面に隠れている場合に備えて、前面にフォーカスを移す
    win.focus();
  }
}

注意点として、この「再利用」はウィンドウ名が一致している場合のみ機能します。第2引数を毎回_blankにしていると、クリックのたびに新しいタブが際限なく増えていくため、「プレビュー用のタブは1つに固定したい」といった要件では、_blankではなく固定の文字列(例:previewWindow)を指定するのがポイントです。また、URLが変わっても同じウィンドウ名であれば同じタブ内でページが遷移するだけなので、都度focus()を呼んでユーザーの目線を新しい内容に誘導する配慮も忘れないようにしましょう。

クロスドメイン環境で安全に親子通信を行うwindow.postMessageの実装方法

親ウィンドウと子ウィンドウが同一オリジン(同じドメイン)であればwindow.openerや戻り値のオブジェクトから直接プロパティにアクセスできますが、異なるドメイン間(クロスオリジン)では、ブラウザの同一オリジンポリシーにより直接アクセスできません。この制約を安全に回避するための標準的な手段がwindow.postMessage()です。

// 【親ウィンドウ側】子ウィンドウを開き、メッセージを受け取る準備をする
const childWindow = window.open("https://child.example.com", "_blank");

// 子ウィンドウからのメッセージを受信するリスナーを登録
window.addEventListener("message", (event) => {
  // 送信元オリジンを必ず検証する(セキュリティ上、最重要のチェック)
  if (event.origin !== "https://child.example.com") {
    return; // 想定外のオリジンからのメッセージは無視する
  }
  console.log("子ウィンドウから受信:", event.data);
});
// 【子ウィンドウ側】親ウィンドウ(opener)へメッセージを送信する
if (window.opener) {
  // 第2引数には、送信先として許可するオリジンを明示的に指定する
  window.opener.postMessage(
    { type: "FORM_SUBMITTED", payload: { id: 123 } },
    "https://parent.example.com"
  );
}

postMessageを実装する際に最も見落とされがちなのが、受信側でのevent.originの検証です。この検証を省略すると、悪意のある第三者のページから偽装したメッセージを受け取ってしまい、意図しない処理が実行されるリスクがあります。第2引数(送信先オリジン)を"*"(ワイルドカード)に設定すれば動作はしますが、これは「誰にでもメッセージを送ってよい」という意味になり本番環境では推奨されません。開発時は動作確認のために"*"を使うことがあっても、リリース前には必ず具体的なオリジンを指定するように修正してください。この一手間が、後述するセキュリティ対策(noopener・noreferrer)とあわせて、安全な別ウィンドウ実装の土台になります。

Window: postMessage() メソッド - Web API | MDN
window.postMessage() は、 Window オブジェクト間で安全にオリジン間通信を可能にするためのメソッドです。例えば、ポップアップとそれを表示したページの間や、iframe とそれが埋め込まれたページの間での通信に使うことができます。

コストパフォーマンスに優れた高性能なレンタルサーバー

【Hostinger】

セキュリティ対策:noopener・noreferrer の正しい指定方法

window.open()target="_blank"で開いた別タブは、便利な反面、正しくセキュリティ対策をしないと「タブジャッキング(reverse tabnabbing)」と呼ばれる攻撃の入り口になり得ます。結論として、外部サイトへのリンクを別タブで開く際は、必ずnoopenernoreferrerをセットで指定することが、コードレビューでも指摘されないための最低限のルールです。ここでは攻撃の仕組みから、HTML・JavaScript両方での正しい実装方法まで解説します。

セキュリティ対策:noopener・noreferrer の正しい指定方法

タブジャッキング・reverse tabnabbing 攻撃の仕組みと危険性

target="_blank"window.open()で新しいタブを開いた場合、デフォルトの挙動によっては、開かれた側(子ウィンドウ)のページからwindow.openerというプロパティを通じて、開いた元のページ(親ウィンドウ)を操作できてしまうことがあります。

具体的な攻撃のイメージは以下の通りです。

  1. 悪意のあるサイトが、一見無害なリンクを設置する
  2. ユーザーがそのリンクをクリックし、新しいタブで悪意のあるサイトが開く
  3. 新しく開いたタブ(悪意のあるサイト)が、window.opener.locationを書き換え、元のタブ(親ウィンドウ)を偽のログインページに差し替える
  4. ユーザーは元のタブに戻った際、URLバーは正規のサイトのままなのに、表示されている中身だけが偽のログインページになっていることに気づかず、IDやパスワードを入力してしまう

この攻撃が厄介なのは、ユーザーが見ているタブのURLは変わらないため、フィッシングサイトだと気づきにくい点にあります。「新しいタブを開いただけなのに、元のタブが乗っ取られる」という直感に反する挙動のため、対策を知らないと防ぎようがありません。

rel=”noopener noreferrer” を付与するHTMLとJavaScriptの両方の実装

このリスクへの対策はシンプルで、開かれた側のページからwindow.openerにアクセスできないようにすることです。HTMLの<a>タグではrel属性にnoopenernoreferrerを指定します。

<!-- HTML側の対策:target="_blank"を使う場合は必ずrel属性を併記する -->
<a href="https://external-site.example.com" target="_blank" rel="noopener noreferrer">
  外部サイトへのリンク
</a>
  • noopener: 新しく開いたページからwindow.openernullにし、親ウィンドウへの参照を断ち切る
  • noreferrer: 上記に加えて、リンク元のURL情報(リファラー)が遷移先に送信されないようにする

JavaScriptのwindow.open()で同様の対策を行う場合は、第3引数(windowFeatures)にnoopenernoreferrerを指定します。

// JavaScript側の対策:第3引数にnoopener,noreferrerを指定する
window.open(
  "https://external-site.example.com",
  "_blank",
  "noopener,noreferrer"
);

なお、モダンブラウザ(Chrome、Firefox、Safariの最新版)では、target="_blank"を指定した<a>タグにrel="noopener"を明示的に書かなくても、デフォルトでnoopener相当の挙動になるよう仕様が変更されています。しかし、この挙動はブラウザのバージョンに依存するため、古いブラウザとの互換性やコードレビューでの明確さを考慮すると、明示的にrel="noopener noreferrer"を記述しておくことを強く推奨します。「デフォルトで安全だから書かなくていい」と判断してしまうと、対応ブラウザの範囲が変わった際に思わぬ脆弱性を生む可能性があるためです。

window.opener を使った親ウィンドウ操作のリスクと noopener 無効化の対策

前述の通り、window.openerは本来、親子ウィンドウ間で正当な連携を行うための機能です。しかし、この参照が悪用されると前述のタブジャッキングにつながるため、信頼できない外部サイトへのリンクには常にnoopenerを付与し、window.openerへのアクセス自体を遮断することが基本方針になります。

// 自社の管理画面など、信頼できる同一オリジンの子ウィンドウとの連携では
// window.openerを使った直接操作が有効なケースもある
if (window.opener && !window.opener.closed) {
  // 親ウィンドウ側の関数を呼び出して、処理結果を伝える(同一オリジンの場合のみ)
  window.opener.postMessage({ status: "completed" }, window.location.origin);
}

一方で、外部の信頼できないサイトへのリンクに対して誤ってnoopenerを付け忘れると、window.openerが有効なまま残ってしまいます。コードレビューチェックリストとしては、「target="_blank"またはwindow.open()を新規に書いたら、必ずnoopenernoreferrerがセットになっているかを確認する」というルールを徹底することをおすすめします。これは今回紹介したセキュリティ対策の中でも、最も見落とされやすく、かつ最も基本的な確認項目です。

WEBCOACH|副業・フリーランス特化型のオンラインWebデザインスクール

実践的な活用シーン別の実装例

ここまで基本仕様・ポップアップブロック対策・セキュリティ対策を解説してきました。ここからは、実務で実際によく遭遇する4つのシーン(PDFプレビュー、TypeScript、jQuery、バナー広告)について、それぞれの実装パターンを具体的なコードとともに紹介します。結論として、基本の書き方は共通しているものの、シーンごとに「型」「ライブラリの流儀」「広告出稿先の仕様」といった個別の注意点があるため、案件に合わせて該当箇所を参照してください。

実践的な活用シーン別の実装例

PDF・画像・Blob URLを別タブでプレビュー表示する方法

サーバーから返却されたPDFファイルや画像を、ダウンロードさせずにその場でプレビュー表示したいというニーズは非常に多くあります。静的なURLであればそのままwindow.open()に渡すだけですが、APIレスポンスとして受け取ったバイナリデータ(Blob)を表示したい場合は、URL.createObjectURL()を使ってBlob URLを生成します。

// 静的なPDFファイルのURLを別タブでプレビュー表示する場合
window.open("https://example.com/files/document.pdf", "_blank", "noopener,noreferrer");
// APIから取得したPDFのBlobデータを別タブでプレビュー表示する場合
async function previewPdfFromApi() {
  const response = await fetch("/api/generate-pdf");
  const blob = await response.blob();

  // BlobデータからブラウザでアクセスできるURL(Blob URL)を生成する
  const blobUrl = URL.createObjectURL(blob);

  // 生成したBlob URLを新しいタブで開く
  const newWin = window.open(blobUrl, "_blank", "noopener,noreferrer");

  if (newWin) {
    // メモリリークを防ぐため、一定時間後にBlob URLを解放する
    newWin.addEventListener("load", () => {
      setTimeout(() => URL.revokeObjectURL(blobUrl), 60000);
    });
  }
}

ここでよく見落とされがちなのが、URL.revokeObjectURL()によるメモリ解放です。Blob URLは発行したままにしておくとブラウザのメモリを占有し続けるため、プレビュー用タブでの表示が完了したと想定できるタイミングで解放する処理を入れておくと、長時間利用されるアプリケーションでのメモリリークを防げます。なお、前述の非同期処理とポップアップブロックの関係上、fetch完了後にwindow.open()を呼ぶ今回のコードはブロックされる可能性があるため、実際の実装では先に空ウィンドウを開いておく手法と組み合わせることをおすすめします。

TypeScriptでwindow.open()を使う際のWindowProxy型エラー対処法

TypeScriptでwindow.open()を使うと、戻り値の型はWindow | nullとして推論されます。この型を正しく扱わずにnewWin.focus()のようにアクセスすると、「オブジェクトは ‘null’ である可能性があります」といった型エラーが発生します。

// 戻り値の型は Window | null として推論される
const newWin: Window | null = window.open("https://example.com", "_blank");

// null チェックを行わずにアクセスするとコンパイルエラーになる
// newWin.focus(); // Error: Object is possibly 'null'.

// 正しくは、null チェック(オプショナルチェイニング)を行ってからアクセスする
newWin?.focus();

// 早期リターンで明示的にガードする書き方も可読性が高くおすすめ
function openAndFocus(url: string): void {
  const win = window.open(url, "_blank", "noopener,noreferrer");
  if (!win) {
    console.warn("ウィンドウを開けませんでした");
    return;
  }
  win.focus();
}

TypeScriptのエラーは一見面倒に感じますが、「ポップアップブロックによってnullが返ってくる可能性」をコンパイル時に強制的に意識させてくれる、実は親切な仕組みです。as Windowのような型アサーションで無理やりnullの可能性を握りつぶすのではなく、オプショナルチェイニング(?.)や早期リターンで正しくガードすることで、本番環境でのランタイムエラーを未然に防げます。

jQuery環境でwindow.open()をイベントハンドラと組み合わせる書き方

レガシーな案件では、jQueryのイベントハンドラの中でwindow.open()を呼び出す場面もまだ存在します。基本的な考え方はネイティブJavaScriptと変わらず、clickイベントハンドラの中で同期的に呼び出すことがポイントです。

// jQueryのclickイベントハンドラ内でwindow.open()を呼び出す
$("#openButton").on("click", function (event) {
  event.preventDefault(); // aタグ等のデフォルト動作をキャンセルする場合

  const url = $(this).data("url"); // data-url属性からリンク先を取得
  const newWin = window.open(url, "_blank", "noopener,noreferrer");

  if (!newWin) {
    alert("ポップアップがブロックされました");
  }
});

jQuery自体にはwindow.open()をラップする専用APIは用意されていないため、上記のようにイベントハンドラの中でネイティブのwindow.open()をそのまま呼び出す形で問題ありません。注意点は、$.ajax()のコールバック(非同期処理)の中でwindow.open()を呼ぶと、前述の通りブロックされやすくなることです。jQueryを使っている案件は歴史が長く、$.ajaxの成功コールバック内にwindow.open()が書かれているレガシーコードも少なくないため、改修時にはブロックされていないか実機で確認することをおすすめします。

バナー広告制作におけるwindow.clicktagを使った遷移処理の実装

Web広告(特にディスプレイ広告・バナー広告)の制作では、広告配信プラットフォーム側が遷移先URLを動的に注入する仕組みとしてclicktagという変数がよく使われます。window.clicktagをそのままwindow.open()に渡すのが基本パターンです。

// グローバル変数clicktagは、広告配信プラットフォーム側から動的に注入される
// (配信先によっては window.clickTag のように大文字小文字が異なる場合があるので要注意)
var clickTag = clickTag || "https://example.com/lp/";

document.getElementById("bannerLink").addEventListener("click", function () {
  // clicktagで指定されたURLを新しいタブで開く
  window.open(clickTag, "_blank", "noopener,noreferrer");
});

バナー広告制作で特に注意すべき点は、配信プラットフォーム(Google広告、Yahoo!広告など)ごとにclicktagの変数名の記法(clickTagclickTAGか等)や実装ルールが微妙に異なることです。入稿前には必ず配信先プラットフォームの最新の入稿規定を確認し、指定された変数名・実装方法に厳密に従うようにしてください。ここでの実装ミスは、審査落ちや正しくクリック計測が行われない原因に直結します。

よくある質問(FAQ)

ここでは、記事本文では触れきれなかった細かい疑問や、実際に検索されることが多い関連キーワードをもとに、Q&A形式で回答します。

window.openを書いたのに効かない・動かないのはなぜですか?

最も多い原因は「ポップアップブロック」です。onclickイベントハンドラの外(ページ読み込み時やsetTimeoutの中など)で実行していないか、fetchasync/awaitなどの非同期処理の後に呼び出していないかを確認してください。また、window.open()の戻り値がnullになっていないかコンソールで確認することで、ブロックされているかどうかを切り分けられます。第2引数(ターゲット名)に誤ってwindowFeatures用の文字列を渡していないかも、あわせてチェックするポイントです。

window.openのポップアップブロックを解除する方法はありますか?

ユーザー側でブラウザの設定からポップアップブロックを解除することは可能ですが、Webサイト制作者側からユーザーに設定変更を強制することはできません。開発者側での対策としては、本文で紹介した「クリックイベント内で同期的にwindow.open()を呼び出す」実装に修正することが最も確実な解決策です。どうしても非同期処理を挟む必要がある場合は、「先に空のウィンドウを開いておき、後からlocationを書き換える」パターンを使ってください。

window.openがスマホ(iPhone・Android)で開かない・別タブにならないのはなぜですか?

iOS SafariはPCのブラウザよりもポップアップブロックの判定が厳しく、非同期処理を挟んだwindow.open()はブロックされやすい傾向があります。また、LINEやInstagramなどのアプリ内ブラウザ(WebView)では、window.open()自体が無効化されていたり、新しいタブという概念がなく同一画面内で開かれたりする場合もあります。スマホ向けの実装では、実機での動作確認を必ず行い、アプリ内ブラウザで開かれる可能性がある場合は<a target="_blank">とあわせてフォールバック的な導線(コピー用のURL表示など)を用意しておくと安心です。

window.open()で開いたウィンドウのサイズや位置が指定通りにならないのはなぜですか?

widthheightlefttopを指定しても、ユーザーの画面の作業領域からはみ出す指定をした場合、ブラウザ側で自動的に位置やサイズが補正され、画面外に一部でもはみ出さないように調整されます。また、Chromeの最新版ではpopupの指定がない状態でscrollbarstoolbarといったレガシーな機能名を指定しても、見た目の細かい制御には反映されず、単に「ポップアップとして開くかどうか」の判定にのみ影響する仕様になっています。UIを厳密に制御したい場合は、期待通りの挙動になっているか主要ブラウザ(Chrome・Safari・Firefox)でそれぞれ確認することをおすすめします。

window.opener(ウィンドウオープナー)とは何ですか?何に使うのですか?

window.openerは、window.open()によって開かれた子ウィンドウ側から、開いた元の親ウィンドウを参照するためのプロパティです。同一オリジンの管理画面同士でデータを連携したい場合などに使われますが、外部の信頼できないサイトへのリンクでこの参照が残っていると、本文で解説した「タブジャッキング」のリスクにつながります。外部サイトへのリンクには必ずnoopenerを指定し、window.openerへのアクセスを遮断しておくことが安全な実装の基本です。

noopenerを指定すると、window.openの戻り値やpostMessageによる通信に影響はありますか?

はい、大きな影響があります。noopenerを指定してwindow.open()を実行すると、セキュリティ上の理由から親ウィンドウと子ウィンドウの参照が完全に切断されます。

このため、子ウィンドウのwindow.openerがnullになるだけでなく、親ウィンドウ側で受け取るwindow.open()の戻り値も強制的にnullになります。戻り値がnullになるため、親ウィンドウから開いた子ウィンドウに対してpostMessage()でメッセージを送信したり、close()で画面を閉じたりすることはできなくなります。

もし、開いた別タブと通信を行ったり、親側からタブを閉じる制御が必要な場合は、対象が自社システムなどの信頼できるサイトであることを前提に、noopenerを指定せずに実装する必要があります。その上で、通信にはpostMessage()を使用し、必ずevent.originを検証して安全性を担保する設計にしてください。

window.open()とlocation.hrefやwindow.location.assign()はどう違いますか?

window.open()は新しいタブ・ウィンドウを生成して別のブラウジングコンテキストでページを開くのに対し、location.hrefwindow.location.assign()は現在表示しているタブ自体のURLを書き換えて、同一タブ内でページ遷移させる点が根本的に異なります。フォーム送信後のリダイレクトのように「同じタブで次のページに進みたい」場合はwindow.locationを、「元のページを表示したまま別の情報を並行して見せたい」場合はwindow.open()を使う、という使い分けが基本になります。

まとめ

window.open()は、JavaScriptから新しいタブやウィンドウを開くために利用できる便利なAPIです。基本的な構文はシンプルですが、実際のWeb制作ではポップアップブロックや非同期処理、セキュリティなども考慮する必要があります。

特に重要なのは、単にwindow.open()を呼び出せばよいわけではなく、用途に応じて適切な方法を選ぶことです。単純にリンクを別タブで開くだけなら<a target="_blank">が適しているケースもあります。一方、JavaScriptによる条件分岐やウィンドウの制御が必要な場合はwindow.open()が役立ちます。

この記事の重要ポイントを整理すると、以下のとおりです。

重要ポイント

  • window.open()はJavaScriptから新しいタブやウィンドウを開くためのAPI
  • 第1引数でURL、第2引数でターゲット名、第3引数でウィンドウの設定を指定できる
  • _blankを指定しても、実際にタブ・ウィンドウのどちらで開くかはブラウザ側の判断に左右される
  • 単純な別タブへのリンクなら、<a target="_blank">も有力な選択肢
  • ユーザー操作と直接関係しないタイミングで実行すると、ポップアップブロックの対象になりやすい
  • fetchasync/awaitなどの非同期処理後に開く場合は、ポップアップブロックを考慮した実装が必要
  • window.open()の戻り値を確認することで、ウィンドウを開けなかったケースを検知できる
  • 開いたウィンドウはwindow.close()closedプロパティを使って管理できる
  • 同じウィンドウ名を指定すれば、既存のウィンドウを再利用できる
  • 親子ウィンドウ間で通信する場合は、クロスオリジン環境も考慮してwindow.postMessage()を利用する
  • 外部ページを開く際はnoopenernoreferrerを理解し、window.openerによるセキュリティリスクにも注意する
  • PDF・画像・Blob URLなどを別タブでプレビューする用途にも応用できる
  • TypeScriptではwindow.open()の戻り値がWindowProxy | nullになる点を考慮して実装する

window.open()は「別タブを開くためのコード」とだけ覚えるのではなく、ポップアップブロック・セキュリティ・ユーザー体験まで含めて使い分けることが大切です。基本的な仕組みを理解しておけば、WebサイトやWebアプリのさまざまな場面で、安全かつ適切に活用できるようになります。

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