Javascript

厄介なポップアップブロックを無理矢理回避する方法

2010/12/01


window.openで新しいウィンドウを開きたいのに、ポップアップブロックに邪魔されて開けないときがあります。
この解決方法をEvernoteサイトメモリーの対応をしているときに発見したので紹介します。

なぜ開かないのか

開かないパターンを知るために、いくつかサンプルを用意しました。

開くパターン

1)直接window.openを実行する

ウィンドウを開く

<a href="javascript:void(0)" onclick="window.open('http://www.mapion.co.jp', '_blank', 'width=500,height=480');return false;">ウィンドウを開く</a>

2)関数内で素直に実行

ウィンドウを開く

<script type="text/javascript">
function openWindow() {
    window.open('http://www.mapion.co.jp', '_blank', 'width=500,height=480');
}
</script>
<a href="javascript:void(0)" onclick="openWindow();return false;">ウィンドウを開く</a>

開かないパターン

1)タイマー内で実行(Firefoxでは開きます)

ウィンドウを開く

<script type="text/javascript">
function openWindowTimer() {
    setTimeout(function(){
        window.open('http://www.mapion.co.jp', '_blank', 'width=500,height=480');
    }, 100);
}
</script>
<a href="javascript:void(0)" onclick="openWindowTimer();return false;">ウィンドウを開く</a>

2)非同期コールバック内で実行

ウィンドウを開く

<script type="text/javascript">
function openWindowAsynchronous() {
    $.get('/', function(data){
        window.open('http://www.mapion.co.jp', '_blank', 'width=500,height=480');
    });
}
</script>
<a href="javascript:void(0)" onclick="openWindowAsynchronous();return false;">ウィンドウを開く</a>

3)onload内で実行

ChromeとSafariで開かないことを確認しています。コードは割愛。

開かないパターンを上記の例から推測すると以下のようになります。

  • ユーザーが起こしたアクションではない
  • 途中で(非同期やコールバックなどのため)処理が分断されている

※一度ポップアップがブロックされると、その後もブロックし続けるブラウザもあるので注意。

ポップアップブロックを回避する裏技

一番妥当なのは直接実行させる範囲内に収めることですが、それだけではどうしても対応できない場合があります。
例えば、クリック→非同期で必要なデータを取得→window.openなど。
そこで、裏技です。

  • 非同期実行のコールバックでwindow.openを指定せず、取得したデータのみ変数に格納
  • ブロックされないタイミングで、先に空のwindowを開く
  • 非同期取得時に格納されるデータをsetIntervalで監視。
    値の取得を確認できたら、formを作成、actionとmethodとtarget(先に開いている空のwindow)を指定→bodyに追加してsubmitで送信する。

以下サンプル

ウィンドウを開く

<script type="text/javascript">
function openWindowSendForm() {
    var tmp;
    $.get('/', function(data){
        tmp = data;
    });
    // ここで先に開きます。 targetは必ず指定。
    window.open('', 'openTest', 'width=500,height=480,resizable=yes');
    
    var count = 0;
    var timerID = setInterval(function(){
        if (tmp) {
            // 非同期のデータが取得できたことを確認したらフォームからデータを送信
            sendData();
            clearInterval(timerID);
            return;
        }
        
        count++;
        if (count > 10) clearInterval(timerID);
    }, 100);
}
function sendData(){
    var attributes = {
        action : "http://www.google.co.jp",
        method : "get",
        target : "openTest"
    }
    
    var form = document.createElement("form");
    for (var key in attributes) {
        form.setAttribute(key, attributes[key]);
    }
    form.style.display = "none";
    var body = document.getElementsByTagName("body")[0];
    body.appendChild(form);
    form.submit();
}
</script>
<a href="javascript:void(0)" onclick="openWindowSendForm();return false;">ウィンドウを開く</a>

確認できる範囲のブラウザでブロックされずWindowが開きます。
ポイントは、ブロックされないところで空のwindowをtarget名を指定して開くこと。
そして、指定したtargetに向けてformを投稿することです。

※ただしこの方法ではonloadのブロックには対応しません。

実は、わざわざフォームを作成せずに、再度window.openを実行しても問題なく実行されたりします。 ただ、Mac版のChromeかSafariでうまく動かないことがあった(気がした)ので、確実に開く方法としてフォームを使った方法を紹介しました。

このやり方もブラウザの隙を突いている気がしないでもないので、いずれブロック対象に指定されるかもしれません。
使う場合はそのあたりのことを考慮に入れておいてください。