2023年3月3日

ポップアップとウィンドウメソッド

ポップアップウィンドウは、利用者に追加のコンテンツを見せるための最も古い方法の1つです。

基本的には次のように実行するだけです:

window.open('http://javascript.info/')

… すると、指定された URL で新しいウィンドウが開きます。ほとんどのモダンブラウザは、別ウィンドウではなく新しいタブとして開くよう設定されています。

ポップアップはとても古くから存在します。当初の考えは、メインのウィンドウを閉じることなく別のコンテンツを表示することでした。現時点では、それをするための他の方法があります: fetch を使うことでコンテンツを動的に読み込むことができ、それを動的に生成された <div> の中で表示することができます。ですから、ポップアップは私達が普段使用するものではありません。

さらにポップアップは、複数のウィンドウを同時には表示しないモバイルデバイスでは手際を要します。

それでも、ポップアップがいまだに使われるタスクが存在します。例えば OAuth 認証(Google や Facebook などへのログイン)。なぜなら:

  1. ポップアップは独立した JavaScript 環境を持つウィンドウです。ですから、第三者の信頼されていないサイトから開くポップアップは安全です。
  2. ポップアップを開くことは非常に簡単です。
  3. ポップアップはナビゲート(URLの変更)可能で、ポップアップを開いたウィンドウにメッセージを送ることができます。

ポップアップブロック

過去、悪意のあるサイトはポップアップを大いに乱用しました。悪意のあるページは広告を含むウィンドウを何度も開く事ができました。そのため、現在多くのブラウザはポップアップをブロックし、ユーザを守ろうとしています。

ほとんどのブラウザは、onclick などユーザがトリガーしたイベントハンドラ外から呼ばれた場合には、ポップアップをブロックします。

これについて考える場合、少し注意が必要です。もしコードが直接 onclick 内にあればそれは簡単です。しかし、ポップアップは setTimeout で開くでしょうか?

例えば:

// ポップアップはブロックされます
window.open('https://javascript.info');

// ポップアップは許可されます
button.onclick = () => {
  window.open('https://javascript.info');
};

このように、ユーザは望まないポップアップからある程度は守られていますが、その機能は完全には無効にされていません。

window.open

ポップアップを開く構文は次の通りです: window.open(url, name, params):

url
新しいウィンドウでロードする URL
name
新しいウィンドウの名前。各ウィンドウは window.name を持っており、ここでポップアップに使うウィンドウを指定することができます。すでに同じ名前のウィンドウがあった場合、そこで指定された URL が開きます。なければ新しいウィンドウが開きます。
params
新しいウィンドウの設定文字列。カンマで区切られた設定を含みます。params の中にスペースを入れてはいけません。例: width:200,height=100.

params の設定:

  • ポジション:
    • left/top (数値) – 画面上のウィンドウの左上隅の座標。新しいウィンドウを画面外に配置することはできない、という制限があります。
    • width/height (数値) – 新しいウィンドウの width と height 。 最小の width/height の制限があるので、, 見えないウィンドウを作成することはできません。
  • ウィンドウの機能:
    • menubar (yes/no) – 新しいウィンドウで、ブラウザのメニューを表示します/非表示にします。
    • toolbar (yes/no) – 新しいウィンドウで、ブラウザナビゲーション(戻る/進む/更新など)を表示します/非表示にします。
    • location (yes/no) – 新しいウィンドウで、URL フィールドを表示します/非表示にします。FF と IE はデフォルトでは隠すことは許可されていません。
    • status (yes/no) – ステータスバーを表示します/非表示にします。ほとんどのブラウザは強制的に表示させます。
    • resizable (yes/no) – 新しいウィンドウのリサイズを無効にします。非推奨です。
    • scrollbars (yes/no) – 新しいウィンドウのスクロールバーを無効にします。非推奨です。

あまりサポートされていないブラウザ固有の機能も数多くありますが、通常は使用されていません。例については、MDN の window.open を確認してみてください。

例: 最小限のウィンドウ

ブラウザがどの機能の無効化を許容するか、最小セットの機能でウィンドウを開いてみましょう。:

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=0,height=0,left=-1000,top=-1000`;

open('/', 'test', params);

ここでは、ほとんどの "ウィンドウの機能 は無効にされ、ウィンドウは画面外に配置されています。実行して実際に何が起きるのかを見てください。ほとんどのブラウザはゼロ値の width/height や画面外の left/top といったおかしなものを “直します”。例えば、Chrome はフルスクリーンになるよう、画面幅/高さでウィンドウを開きます。

通常の配置オプションと妥当な width, height, left, top 座標を追加しましょう。:

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;

open('/', 'test', params);

ほとんどのブラウザは要求に従って上記の例を表示します。

設定が省略された際のルール:

  • open 呼び出しで 3つ目の引数がない場合、もしくはそれが空の場合、デフォルトのウィンドウパラメータが使われます。
  • params の文字列はあるが、一部の機能の yes/no が省略されている場合、省略された機能はブラウザで許可されていれば無効になります。そのため、params を指定する場合には、明示的に必要なすべての機能に yes を設定してください。
  • params に left/top がない場合、ブラウザは最後に開いたウィンドウの近くに新しいウィンドウを開こうとします。
  • width/height がない場合、新しいウィンドウは最後に開いたウィンドウと同じサイズになります。

ポップアップにアクセスする

open 呼び出しは、新しいウィンドウへの参照を返します。それはプロパティを操作したり、位置を変えたりといったことをするのに利用できます。

この例では、ポップアップの内容を JavaScript で生成します:

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write("Hello, world!");

またこちらでは、コンテンツをロード後に変更します:

let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.focus();

alert(newWindow.location.href); // (*) about:blank, 読み込みはまだ始まっていません

newWindow.onload = function() {
  let html = `<div style="font-size:30px">Welcome!</div>`;
  newWindow.document.body.insertAdjacentHTML('afterbegin', html);
};

注意してください: window.open の直後は、新しいウィンドウはまだ読み込まれていません。これは (*) の行にある alert で示されています。ですから、変更するために onload を待っています。 newWin.document 用に DOMContentLoaded ハンドラを使うこともできます。

同一生成元ポリシー

ウィンドウは、同じ元(同じプロトコル://ドメイン:ポート)から生成された場合のみ、互いのコンテンツに自由にアクセスすることができます。

そうでなければ、例えばメインウィンドウが site.com から生成され、ポップアップが gmail.com から生成された場合、ユーザの安全性のため不可能となります。詳しくは、 ウィンドウを跨いだやり取り の章を確認してください。

開いた元(opener)のウィンドウにアクセスする

ポップアップは window.opener 参照を用いることでも “opener” ウィンドウにアクセスできます。ポップアップ以外のすべてのウィンドウの場合、それは null です。

以下のコードを実行すると、 opener ウィンドウ(現在のウィンドウ)の内容が “Test” に置き換わります:

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write(
  "<script>window.opener.document.body.innerHTML = 'Test'<\/script>"
);

ですから、ウィンドウ間の接続は双方向です: メインウィンドウとポップアップは互いへの参照を持っています。

ポップアップを閉じる

ウィンドウを閉じるには: win.close()

ウィンドウが閉じられたかを確認するには: win.closed

技術的には、close() メソッドはどの window でも利用可能ですが、windowwindow.open() で生成されたものでない場合、window.close() はほとんどのブラウザで無視されます。ですからこれはポップアップでのみ機能します。

closed 属性は、ウィンドウが閉じられた場合 true です。これはポップアップ(あるいはメインウィンドウ)がまだ開かれているかどうかを確認するのに便利です。ユーザはいつでもそれを閉じることができ、私達のコードではその可能性を考慮に入れるべきです。

このコードはウィンドウを開いた後、閉じます。:

let newWindow = open('/', 'example', 'width=300,height=300')

newWindow.onload = function() {
  newWindow.close();
  alert(newWindow.closed); // true
};

移動とリサイズ

ウィンドウを動かす/リサイズするメソッドがあります:

win.moveBy(x,y)
ウィンドウを現在の位置から相対的に x ピクセル右に、 y ピクセル下に動かします。負の値は許可されます(左/上に動きます)。
win.moveTo(x,y)
ウィンドウをスクリーン上の座標 (x,y) に動かします。
win.resizeBy(width,height)
ウィンドウを現在の大きさから相対的に与えられた width/height だけリサイズします。負の値は許可されます。
win.resizeTo(width,height)
ウィンドウを与えられた大きさにリサイズします。

window.onresize イベントもあります。

ポップアップのみ

乱用を防ぐため、ブラウザは通常これらのメソッドをブロックします。これらは私達が開いた、追加のタブのないポップアップに対してのみ確実に機能します。

最小化/最大化はありません

JavaScript にはウィンドウを最小化あるいは最大化する方法がありません。これらの OS レベルの機能はフロントエンド開発者から隠されています。

移動/リサイズのメソッドは最大化/最小化されたウィンドウに対しては効きません。

ウィンドウのスクロール

私達は既にウィンドウサイズとスクローリングの章で、ウィンドウをスクロールすることについて述べてきました。

win.scrollBy(x,y)
ウィンドウを現在のスクロールから相対的に x ピクセル右に、 y ピクセル下にスクロールします。負の値は許可されます。
win.scrollTo(x,y)
ウィンドウを与えられた座標 (x,y) にスクロールします。
elem.scrollIntoView(top = true)
ウィンドウを、 elem が上部に(デフォルト)、あるいは elem.scrollIntoView(false) の場合は下部に表示されるようにウィンドウをスクロールします。

window.onscroll イベントもあります。

ポップアップへの focus/blur

理論的には、ウィンドウにフォーカスを当てる/外す window.focus()window.blur() メソッドがあります。また、ウィンドウにフォーカスしたり、訪問者が別の場所へ切り替えた瞬間を捉える focus/blur イベントもあります。

けれども、実際これらはかなり制限されています。なぜなら過去に悪意のあるページはこれらを乱用したからです。

例えば、次のコードを見てください。:

window.onblur = () => window.focus();

利用者がウィンドウから出ようとすると(window.blur)、このコードはフォーカスを戻します。この意図は、利用者を window 内に “ロック” することです。

そのため、ブラウザはこのようなコードを禁止し、広告や悪意のあるページからユーザを守るために多くの制限を導入しなければなりませんでした。これらはブラウザによります。

例えば、モバイルブラウザは通常、 window.focus() を完全に無視します。また、ポップアップが新しいウィンドウではなく別のタブで開いた場合、フォーカスは動作しません。

それでも、そのような呼び出しが機能し、役に立つようなユースケースもあります。

例:

  • ポップアップを開く際、newWindow.focus() を行うのは良いアイデアかもしれません。OS/ブラウザの組み合わせによっては、ユーザが新しいウィンドウにいることを保証します。
  • 訪問者が実際にいつ我々の web アプリを利用したかを追跡したい場合、window.onfocus/onblur が使えます。これにより、ページ内でのアクティビティやアニメーションなどを一時停止/再開することができます。しかし、blur イベントは訪問者がウィンドウを切り替えたことを意味しますが、それでも監視できる可能性があることに留意してください。ウィンドウはバックグラウンドにありますが、まだ表示されている可能性があります。

サマリ

ポップアップウィンドウはほとんど使われません。なぜなら代替方法があるからです: 情報をページ内あるいは iframe 内で読み込みます。

もしポップアップを開くつもりならば、それをユーザに伝えることをおすすめします。リンクやボタンの近くに “ウィンドウを開く” アイコンがあれば、訪問者はフォーカスの遷移を念頭に置くことができますし、両方のウィンドウを気に留めておくことができます。

  • ポップアップは open(url, name, params) 呼び出しで開くことができます。これは新しく開かれたウィンドウへの参照を返します。
  • ブラウザは、ユーザアクション外からの open 呼び出しをブロックします。通常、通知が表示されるので、利用者はそれを許可することができます。
  • ブラウザはデフォルトで、新しいタブを開きます。しかし大きさが渡されている場合、ポップアップウィンドウを開きます。
  • ポップアップは window.opener プロパティを利用して、開いた元のウィンドウにアクセスできます。
  • メインウィンドウとポップアップが同じ生成元から来たものである場合、お互いを自由に読み書きすることができます。そうでない場合、お互いの location を変更し、メッセージを使用してやり取りすることができます(この後説明があります)。

ポップアップを閉じるには、close() 呼び出しを使用します。また、ユーザもそれを閉じることができます。その後、 window.closedtrue です。

  • メソッド focus()blur() でウィンドウへフォーカスしたり外したりできますが、いつも機能するとは限りません。
  • イベント focusblur によりウィンドウの内外への切り替えを追跡することができます。しかし、ウィンドウは blur の後、バックグラウンド状態でも見えるかもしれないことに留意してください。
チュートリアルマップ