javaScriptでブラウザバックに対応したモーダルウィンドウを設置する

## モーダルウィンドウの仕様
- 現在、表示しているページを**ブラウザバック**で離れる時にモーダルウィンドウを表示する
- モーダルウィンドウを表示した後は、モーダルウィンドウ内の「閉じる」をクリックすればブラウザバックの機能が実行され、モーダルウィンドウの背景を触ると元の画面(レイヤー下)に戻る。

## サンプルページ
モーダルウィンドウ表示ページを一回クリックして、ブラウザバックすると、モーダルウィンドウが表示される。
https://kaibara.github.io/js-modal/

構造全体は[こちら](https://github.com/kaibara/js-modal)

## モーダルウィンドウ実装の注意点
この記事のような仕様だと、モーダルウィンドウの表示によってユーザーが取りたい行動を阻むことになる。
実装するサイトやページによっては**SEOの順位が下がる可能性がある**ため、モーダルウィンドウの仕様だけでなく、サイトやページの仕様についてもよく検討しておく。

## モーダルウィンドウで表示する内容(htmlとcss)
`html`及び`css`は[【jQuery・CSS】意外と簡単！モーダルウィンドウをプラグインなしで作る | WEBDESIGNDAY](https://webdesignday.jp/inspiration/technique/css/4680/) の内容を参考に作成。

```modal.html
<div class="modal js-modal">
    <div class="modal__bg js-modal-close"></div>
    <div class="modal__content">
        <p>モーダルウィンドウテンプレート</p>
        <a class="js-modal-close" href="">閉じる</a>
    </div>
</div>
```

```style.css
.modal{
    display: none;
    height: 100vh;
    position: fixed;
    top: 0;
    width: 100%;
}
.modal__bg{
    background: rgba(0,0,0,0.8);
    height: 100vh;
    position: absolute;
    width: 100%;
}
.modal__content{
    background: #fff;
    left: 50%;
    padding: 40px;
    position: absolute;
    top: 50%;
    transform: translate(-50%,-50%);
    width: 60%;
}
```

`css`は今回の本題ではないため説明は省く。
解説が見たければ[参考記事](https://webdesignday.jp/inspiration/technique/css/4680/)へ。

## モーダルウィンドウを表示する(javaScript)
### コード全体像

モーダルウィンドウの表示、非表示に関しては[【jQuery・CSS】意外と簡単！モーダルウィンドウをプラグインなしで作る | WEBDESIGNDAY](https://webdesignday.jp/inspiration/technique/css/4680/)を参考に作成。

```modai.js
$(function(){

    // どのようにページが読み込まれたかのフラグ
    var backFlg = window.performance.navigation.type;

    // ブラウザバック以外で読み込まれたらのif
    if(backFlg < 1){
        // 履歴に現在のURLを追加保存する
        history.pushState(null,null,location.href);
    }

    //モーダルウィンドウの表示
    $(window).on('popstate', function(event) {
        $(".js-modal").fadeIn();

        // モーダルウィンドウ内閉じるリンクのリンク先変更
        var mLink = document.getElementsByClassName("js-modal-close")
        mLink[1].setAttribute("onClick", "history.back();return false;");

    });

    // モーダルウィンドウの解除
　　$('.js-modal-close').on('click',function(){
        $(".js-modal").fadeOut();
        return false;
　　});

});
```

### 解説

#### ブラウザバックのチェック

ブラウザバック時にモーダルウィンドウを表示するには、ユーザによるブラウザバックの操作を`javaScript`が検知できるようにする必要がある。
だが、`javaScript`ではブラウザバックを検知できない。
それなら、**ブラウザバックと共に変更されるものを検知すれば良い。**

ということで、ブラウザバックと共に変更される物の中で一番わかりやすいであろう、**履歴の変更**でユーザーのブラウザバックを検知する。
`javaScript`では**`popstateイベント`**で履歴の変更を検知できる。
[ブラウザの「戻る」ボタンをJavascriptで検知するイベント代替案 | Yuta Ihara.com](https://yutaihara.com/archives/512)

```modal.js
// 履歴に現在のURLを追加保存する
history.pushState(null,null,location.href);
```

**`history.pushState(null,null,location.href);`**で履歴に現在のURLを追加する。
**`pushState`をしないと、`popstate`で検知できないので欠かさずに記入。**

ブラウザバックのボタンを右クリックしてみると、現在のページが2つ重なっている(履歴のと現在表示中ので2つ)のがわかる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218398/fc2b3d5b-bd0a-f4d3-eff8-74e8afadf0fb.png)

ここで追加することで、ブラウザバックを押しても、遷移元のページに戻らずにモーダルウィンドウを表示できる

```
例) A(遷移元) → B(現在のページ)

history.pushState();　がない時
A(遷移元) → B(現在のページ)

Bからブラウザバック → A(遷移元)

history.pushState();　がある時
A(遷移元) → B(現在のページ) → B'(history.pushState();で追加した履歴)

B'からブラウザバック → B(現在のページ)

```

[JavaScriptでURLを操作するメモ - Qiita](https://qiita.com/PianoScoreJP/items/fa66f357419fece0e531)
[Webブラウザの [前へ][次へ] ボタンがクリックされ履歴のページが表示されるイベントを検出する - popStateの利用 : JavaScript | iPentec](https://www.ipentec.com/document/javascript-history-popstate)


#### どのようにアクセスされたかのチェック
先に「ブラウザバックでどのようにモーダルウィンドウを表示するか」について話したが、その前にある`backFlg`フラグと`history.pusyState();`を囲むif文の解説をしていく。

```modal.js
// どのようにページが読み込まれたかのフラグ
var backFlg = window.performance.navigation.type;
```

`window.performance.navigation.type`は、このページがどのように読み込まれたかの値を返す。
他のページからのリンクやブックマークからなら`0`,再読み込みなら`1`,ブラウザバックなどの履歴なら`2`,それ以外なら`255`の値を返す。
[PerformanceNavigation.type - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/PerformanceNavigation/type)

```modal.js
// ブラウザバック以外で読み込まれたらのif
if(backFlg < 1){
    // 履歴に現在のURLを追加保存する
    history.pushState(null,null,location.href);
}
```

このif文ではブラウザバック以外、他のページからのリンクや再読み込みでこのページが読み込まれたら、`history.pusyState();`で履歴を追加するようにしている。
何故このようなif文が必要なのか。

それは、**アクセスのされ方でブラウザバックの回数に変化が生じるから**だ。
`window.performance.navigation.type`で`0`が返されるなら、遷移元の上に`history.pusyState();`で履歴が追加されるため、履歴を見ると表示中の物もを含めて、現在のページが2つある状態になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218398/05c02610-74e2-0e32-994f-dbf3886281f9.png)
この状態でブラウザバックすれば、同じページのまま履歴だけが変わったので`popstate`が検知し、`$(window).on('popstate', function(event) {}`の中身が実行される。

なら、`window.performance.navigation.type`が`1`か`2`を返す状態で、`history.pusyState();`が実行されたらどうなるか。
答えは、表示中のページを含めて履歴にそのページが3つ重なる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218398/523b84e3-507d-c812-f588-912996629461.png)

この画像の状態だと、`遷移元ページ`に戻るのに`ブラウザバック → モーダルウィンドウ表示`の流れを2回繰り返す必要が生まれてしまう。

`backFlg`とそれを使用したif文によって、`history.pusyState();`を制御し、どのようなアクセス方法であってもブラウザバックの回数に変化が生じないようにしている。

[JavaScript - ブラウザバック を検知したい｜teratail](https://teratail.com/questions/312782)

#### モーダルウィンドウの表示
現在の履歴を追加できたら、`addEventListener`で`popstate`が呼び出されるのを検知して、呼び出された時の機能を追加する。

```modal.js
$(".js-modal").fadeIn();
```
ここは読んだそのまま。該当要素をフェードインさせる。

```modal.js
// モーダル内閉じるリンクのリンク先変更
var mLink = document.getElementsByClassName("js-modal-link")
mLink[1].setAttribute("onClick", "history.back();return false;");
```

「閉じる」リンクの`class`を読み取って、閉じるリンクにブラウザバックと同じ機能(履歴のひとつ前のに戻る)を追加する。
`setAttribute`での`onClick`の上書きはhtmlファイル側に書いても問題はないけど、モーダルウィンドウの表示に関するjavaScriptの動きはまとめておきたかったのでこちらに記入。

#### モーダルウィンドウを閉じる

```modal.js
// モーダルウィンドウの解除
$('.js-modal-close').on('click',function(){
　　$(".js-modal").fadeOut();
    return false;
});
```

指定した要素がクリックされたら、`fadeIn()`同様、`fadeOut()`で該当要素をフェードアウトさせていく。
`js-modal-close`には、aタグ要素も含まれるので、`return false;`でリンク先に移動しないようにしておく

## 余談
一回ページをクリックしないとブラウザバックを押してもモーダルウィンドウが表示されないのはなんで…?


## 参考文献
- [【jQuery・CSS】意外と簡単！モーダルウィンドウをプラグインなしで作る | WEBDESIGNDAY](https://webdesignday.jp/inspiration/technique/css/4680/)
- [ブラウザの「戻る」ボタンをJavascriptで検知するイベント代替案 | Yuta Ihara.com](https://yutaihara.com/archives/512)
- [JavaScriptでURLを操作するメモ - Qiita](https://qiita.com/PianoScoreJP/items/fa66f357419fece0e531)
- [Webブラウザの [前へ][次へ] ボタンがクリックされ履歴のページが表示されるイベントを検出する - popStateの利用 : JavaScript | iPentec](https://www.ipentec.com/document/javascript-history-popstate)
- [JavaScript - ブラウザバック を検知したい｜teratail](https://teratail.com/questions/312782)