ポップアップウィンドウは、利用者に追加のコンテンツを見せるための最も古い方法の1つです。
基本的には次のように実行するだけです:
window.open('http://javascript.info/')
… すると、指定された URL で新しいウィンドウが開きます。ほとんどのモダンブラウザは、別ウィンドウではなく新しいタブとして開くよう設定されています。
ポップアップはとても古くから存在します。当初の考えは、メインのウィンドウを閉じることなく別のコンテンツを表示することでした。現時点では、それをするための他の方法があります: fetch を使うことでコンテンツを動的に読み込むことができ、それを動的に生成された <div>
の中で表示することができます。ですから、ポップアップは私達が普段使用するものではありません。
さらにポップアップは、複数のウィンドウを同時には表示しないモバイルデバイスでは手際を要します。
それでも、ポップアップがいまだに使われるタスクが存在します。例えば OAuth 認証(Google や Facebook などへのログイン)。なぜなら:
- ポップアップは独立した JavaScript 環境を持つウィンドウです。ですから、第三者の信頼されていないサイトから開くポップアップは安全です。
- ポップアップを開くことは非常に簡単です。
- ポップアップはナビゲート(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
でも利用可能ですが、window
が window.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.closed
は true
です。
- メソッド
focus()
とblur()
でウィンドウへフォーカスしたり外したりできますが、いつも機能するとは限りません。 - イベント
focus
とblur
によりウィンドウの内外への切り替えを追跡することができます。しかし、ウィンドウはblur
の後、バックグラウンド状態でも見えるかもしれないことに留意してください。
コメント
<code>
タグを使ってください。複数行の場合は<pre>
を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。