ブラウザの戻るを押したらモーダルを表示する方法

こんにちは!のせっち@nosecchi01です!

クライアントさんの要望で「ブラウザバックでお客さんが離れてしまう前に、広告を出したい」という要望があるかもしれません。

そういう要望に応えるために、ブラウザバックでモーダルを出現させる方法をご紹介します。

機能としては以下です。

  • ブラウザの戻るを押すと、モーダルが出現
  • モーダルを閉じると戻るを実行

注意点:ブラウザバックの制御はGoogle Chromeではできない

これからご紹介する方法ですが、僕が確認した限りでは(2021年11月現在)Google Chromeでは再現不可能です。

理由は、こちらの記事が詳しいです。

記事で紹介されている元の英文記事では禁止する方向となっていますが、既に実装済みではないかと思います。(確定情報ではないです)

ユーザーの意図していない動作を返すのはユーザー体験としては良くないので、クライアントさんから実装を求められた場合、まずは理由を説明した上でお断りすることをおススメします。

現状ではChromeのみですが、他ブラウザが追随する可能性は十分にあります。

それでも、どうしても、実装が必要な場合は以下をどうぞ!

完成図を確認する

まずはcodepenをご確認ください。

See the Pen 戻るでモーダルを出現させる by Yuki Nose (@yukinouz1) on CodePen.

「前のページに戻る」ボタンを押すと、閉じるボタンが出現します。

モーダルといいつつ、ボタンしか出てきませんが、今回モーダルの見た目は本筋ではないので省略です

codepenでは再現しないですが、「閉じる」を押すと、ブラウザバックしてくれます。

codepenで戻るが再現しない理由は戻るページが存在しないためです。

「アバウトへ」は適当なリンクを付けています。通常のリンクタグではモーダルが出現しないことが確認できます。

一度閉じるを押した後は、「前のページに戻る」ボタンを押しても何も起きなくなります。
戻るのたびにモーダルが出ていたら一生外に出られないので、一度だけ実行するようにしています。

機能をわかっていただけたところで、中身を解説していきます!

解説

javascriptを中心に解説していきます。

まずは全体のコードです。

let modalState = false;

  window.addEventListener("popstate", (e) => {
    // aタグクリックや進むボタンでの遷移時にも呼ばれるので、モーダルが出てないかつstateがpushedの場合のみモーダルを出す
    if (modalState || e.state !== "pushed")
      return;

    // fadeIn/fadeOutはjQueryを使う
    $("#js-modal").fadeIn();
    $(".js-modal-close").on("click", ()=> {
      $("#js-modal").fadeOut();
      modalState = true;
      window.history.back();
    })
  });

  const onload = () => {
    window.history.replaceState("pushed", null, null); // 現在のページにstateを付加してpopstateイベントで判定する
    const entries = performance.getEntriesByType("navigation");
    if (entries.some((item) => item.type === "reload")) {
      return;
    } else {
      window.history.pushState("pushed", null, null); // 戻るボタンを有効化させるためにヒストリを一つ追加する
    }
  };
  window.addEventListener("load", ()=> {
    onload();
  });

ここからは更に分解して解説していきます。

let modalState = false;

modalStateはモーダルが開いたらtrueにします。(後ほど出てきます)モーダルが開いたかどうかを判定して、一度開いた(trueになった)ら二度と戻るでモーダルが出ないようにするためです。

  window.addEventListener("popstate", (e) => {
    // aタグクリックや進むボタンでの遷移時にも呼ばれるので、モーダルが出てないかつstateがpushedの場合のみモーダルを出す
    if (modalState || e.state !== "pushed")
      return;
  });

popstateは戻るボタンを押したかどうかを判定します。

if (modalState) は先ほど解説した通りで、modalState === trueならです。 e.state !== "pushed"は後ほど解説しますが、後半のコードでヒストリを操作するので、state !== "pushed"なら、となります。

どちらかの条件を満たす場合は、return;で処理を抜けます。(モーダルは出さない)

$("#js-modal").fadeIn();
$(".js-modal-close").on("click", ()=> {
  $("#js-modal").fadeOut();
  modalState = true;
  window.history.back();
})

fadeIn()fadeOut()はjQueryが便利なのでjQueryを使っています。

戻るを押したら、#js-modalをfadeInです。

.js-modal-closeで以下の処理を行います。

  • モーダルを閉じる(fadeOut)
  • modalState を trueにする
  • 「戻る」を実行

ここをIDではなくClassにしている理由は、モーダルではよく背景クリックでも閉じるを実行したりするためで、同一class名が存在する前提でclassにしています。

modalState = true
modalStateをtrueにすることで、次に戻るを押してもモーダルが出現しないようにします。

window.history.back();
モーダルを閉じたら、ユーザーが本来意図した戻るを実行してあげます。
モーダルを閉じるだけで、ページに留めることもできますが、ユーザーは本来戻りたいのに、モーダルが出てきている(意図しない挙動)ことになりますからね。

次に進みます。

const onload = () => {
  window.history.replaceState("pushed", null, null); // 現在のページにstateを付加してpopstateイベントで判定する
  const entries = performance.getEntriesByType("navigation");
  if (entries.some((item) => item.type === "reload")) {
    return;
  } else {
    window.history.pushState("pushed", null, null); // 戻るボタンを有効化させるためにヒストリを一つ追加する
  }
};

history.replaceState(state, title[, url]);現在の履歴(ヒストリー)を置換します。

  const entries = performance.getEntriesByType("navigation");
  if (entries.some((item) => item.type === "reload")) {
    return;
  } else {
    window.history.pushState("pushed", null, null); // 戻るボタンを有効化させるためにヒストリを一つ追加する
  }
};

window.history.pushState("pushed", null, null);でpushedをヒストリに追加しています。戻るボタンを右クリックすると、同一ページが追加されていて、ヒストリが追加されていることがわかります。

const entries = performance.getEntriesByType("navigation");
if (entries.some((item) => item.type === "reload")) { return; }
リロードを無視するために書いています。これを書かないとリロードの度にヒストリが増えてしまうので必ず書いておきましょう!

window.addEventListener("load", ()=> {
  onload();
});

定義したonloadを実行しています。

解説は以上です!

お疲れ様でした!

まとめ

・javascriptを使って、戻るを実行した際に何かアクションを起こすことができる。
・Google Chromeでは再現しない。
・ユーザービリティ的によくないので、実装する際は本当に必要な機能なのかよくよく検討する。

この記事を書いた人

のせっち

福岡県出身。早稲田大学卒。創業100年の鉄鋼商社でバンコク駐在最年少抜擢。毎日擦り切れるまで働かなくても幸せに生きている人々を見てフリーランスになりました。
現在は、主にバンコクでWeb制作、物販を中心に生きています。