Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What is going on with this article?
@heppokofrontend

【令和版】window.close() でタブが閉じない時の解決法

開発者「すいません、window.close()が動かないいんですけど…」

ワイ「コンソールになんか書いてないですか?」

Scripts may close only the windows that were opened by them.

開発者「ってでてます。」

ワイ「そこに書いてある通りですね。」

開発者「すみません、英語わかんなくて…」

ワイ「」


なぜ閉じないのかわからない人も、どうすればいいかと相談される人も、window.close()で消耗するのはもうおしまいにしましょう。

もう聞かれた時にURLを投げればいいだけにしておきたいので、この記事を残しておきます。

さて、まずはなぜ解決できないのかを知っていくために、先ほどの会話の中で出てきた英文の日本語訳を確認します。

Google翻訳:スクリプトは、スクリプトによって開かれたウィンドウのみを閉じることができます。

直訳なのでわかりづらいですが、要はJavaScriptで閉じることができるウィンドウは、JavaScriptで開かれたウィンドウのみであると書いてあります。

しかしながら、時々JavaScriptで開いてないウィンドウもwindow.close()で閉じることができる気がします。それはなぜでしょうか?

一旦冷静になって仕様を確認してみましょう。

What is window.close()

A browsing context is script-closable if it is an auxiliary browsing context that was created by a script (as opposed to by an action of the user), or if it is a top-level browsing context whose session history contains only one Document.

参考文献:https://html.spec.whatwg.org/multipage/window-object.html#dom-window-close

ざっくり要約すると、window.close()で閉じることができるのは次のパターンです。

  • JavaScriptによって開かれた場合
  • 履歴が1つしかないトップレベルブラウジングコンテキストの場合

それぞれ1つずつ見ていきます。

JavaScriptで開かれた場合

window.open()1によって開かれた場合がこれに該当します。

ちなみに、window.open()でも、featuresnoopenerが指定されていると「JavaScriptで開かれた場合」に該当しない振る舞いになります。
逆に、a[target="_blank"]でも[rel="opener"]を付与すれば、「JavaScriptで開かれた場合」に該当する振る舞いになります2

こうした実際の動きをみると、「JavaScriptによって開かれた」というよりも、opener3が存在する場合、と言った方が正しいかもしれませんね。

履歴が1つしかないトップレベルブラウジングコンテキストの場合

2つの要素が関係しているので、ここも1つずつ確認していきましょう。

履歴が1つしかない

window.history.length1のときを指します。

これは、タブ(ブラウジングコンテキスト)が開かれてから、どこページにも遷移していないということを表します。つまり、そのブラウジングコンテキストで「戻る」「進む」がどちらか1方向でもできる状態だと、履歴の長さは2以上になります。

if (window.history.length === 1) {
  // It is a top-level browsing context whose session history contains only one Document.
}

トップレベルブラウジングコンテキスト

  • window - 現在のブラウジングコンテキストのWindowProxy
  • window.parent - 親ブラウジングコンテキストのWindowProxy
  • window.top - トップレベルブラウジングコンテキストのWindowProxy

iframeなどでブラウジングコンテキストがネストしているとき、自身のブラウジングコンテキストがトップレベルかを確認するには、次のような方法で確認できます4

if (window === window.top) {
  // It is a top-level browsing context
}

逆に特別なことをしていなければ、常にトップレベルブラウジングコンテキストのはずです。

どちらの状況も満たせない時にウィンドウを閉じるにはどうしたらいいの?

残念ですが閉じることができません5

そういう場合は、そもそも閉じるボタンの実装を見送って「このタブを閉じてください」など、ユーザ自身にページを閉じるよう促す文言を掲載したりするしかないでしょう。

もし、多くのケースで閉じるボタンが有効ではあるものの、特定のケースでのみ前述の条件を満たせないような場合には、代替テキストを表示する方法もあります。

<button type="button" id="close">このボタンを押すとページが閉じるにぇ</button>

<script>
const closeBtn = document.getElementById('close');

closeBtn.addEventListener('click', function () {
  window.close();

  // `window.closed`を参照することでページが閉じられているかを確認できます。
  if (!window.closed) {
    this.textContent = '閉じるのに失敗したぺこだよ…';
  }
});
</script>

あるいは、もともと閉じることができるかを判定して、「閉じるボタン」と「ブラウザのUIで閉じるよう促す文言」を出し分けるのもいいかもしれません。

<p id="close">ブラウザの×ボタンを押してほしいんだワ</p>

<script>
const placeholder = document.getElementById('close');

if (
  window.opener ||
  (
    window === window.top &&
    history.length <= 1
  )
) {
  const closeBtn = document.createElement('button');

  closeBtn.type = 'button';
  closeBtn.textContent = '押したらページが閉じるっちゃま〜〜〜!';
  closeBtn.addEventListener('click', function () {
    window.close();
  });
  placeholder.replaceWith(closeBtn);
}
</script>

テスト用スペース

動いているのが確認したいという方向けにwindow.close()の動きをテストするためのページを用意してみました。

このページでは次の状態の違い、確認できます。

  • トップレベルブラウジングコンテキストかどうか
  • openerの存在
  • 履歴の長さ
  • Script closable(window.close()が利用可能か)

終わりに

わたしたちがwindow.close()閉じない問題を本当の意味で解決する方法は、そもそももっと上流工程を見直してwindow.close()が不要な同線設計になるようにするほかありません。

可能であれば、同線設計の話の部分にも食い込んで提案するようにしていきたいですね😔


  1. https://developer.mozilla.org/ja/docs/Web/API/Window/open
    https://html.spec.whatwg.org/multipage/window-object.html#dom-open 

  2. Google Chrome 89、Firefox 86、Edge 89 で動作確認済み。Safari(14)ではhistoryの挙動が異なります。 

  3. https://developer.mozilla.org/ja/docs/Web/API/Window/opener 

  4. https://html.spec.whatwg.org/multipage/browsers.html#navigating-nested-browsing-contexts-in-the-dom 

  5. 通常のJavaScriptの範疇を超えない場合に限る(ブラウザ拡張を利用するなどはその限りではない) 

4
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
heppokofrontend
へっぽこフロントエンドエンジニアです。 TypeScriptとかWebアクセシビリティに興味があります。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
4
Help us understand the problem. What is going on with this article?