dribbbleのようなモーダル兼詳細ページをRailsで作る(後半)

dribbbleのようなモーダル兼詳細ページをRailsで作る(前半)

こちらの続きです。

残りのタスク

前回記事 の後半にも書きましたが、 改めておさらいです。

Hugo Theme Gallery では、 一応モーダルで表示するのと、別タブで表示するというのは実装できましたが、モーダル表示の際にURLの変更がされていない状態でした。

これは、HTML5のHistory APIを利用することで対応することができます。
History API - Web API | MDN

URLの変更(参照の履歴の追加)と、元の画面に戻る(参照履歴の削除)方法についてそれぞれ説明していきます。

URLの変更(参照の履歴の追加)

モーダルを開いたときにURLをセットする方法です。 pushState

1
2
3
4
5
6
7
const MODAL_STATE = "modal";
document.querySelector("body").addEventListener("click", (outerEvent) => {
  if (outerEvent.target.className === "shot-img shadow-sm") {
    const link = outerEvent.target.parentNode;
    window.history.pushState(MODAL_STATE, link.getAttribute("data-modal-title"), link.getAttribute("href"));
  }
});

window.history.pushState を利用します。 ポイントは、第一引数なのですが、ここはブラウザの戻るボタンを押したときの対応として必要なものとなります。 こちらについては、後述しますので、今は気にしなくて良いです。 (addEventListenerは、jQueryでいうところのdelegateをさせるためにbodyに仕込んでいます。)

元の画面に戻る(参照履歴の削除)方法

こちらは、モーダルを閉じる場合と、ブラウザの戻るボタンを押した場合の2種類のケースを考慮する必要があります。

1.モーダルを閉じる場合

モーダル右上のxボタンや、モーダルの外をクリックした時に自動でモーダルが閉じられます。 この閉じる機能に history.back() を追加すれば良いです。

1
2
3
4
5
6
const detailModalEl = document.getElementById("js-detail-modal");
detailModalEl.addEventListener("hide.bs.modal", () => {
  if (window.history.state === MODAL_STATE) {
    window.history.back();
  }
});

具体的には、Modal (モーダル) · Bootstrap v5.0 に記載されている、 hide.bs.modal を利用します。 (hidden.bs.modal も用意されていますが、こちらはモーダルが非表示になるのを待つので、ワンテンポ遅くなります。)

ここで、 history.state が出てきますが、後述のため一旦説明は省きます。

2.ブラウザの戻るボタンを押した場合

ブラウザの戻るボタンを押すと、history.back() が実行されるため、URLは正しく戻りますが、 モーダルは残ったままです。

ですので、このモーダルを消せば良さそうです。

1
2
3
4
5
6
window.addEventListener("popstate", () => {
  const detailModal = bootstrap.Modal.getInstance(detailModalEl);
  if (detailModal && detailModal._isShown) {
    detailModal.hide();
  }
});

ここで登場するのが popstate です。これは、history.back() が呼び出された時にトリガーされます。 ですので、ブラウザが開いている時だけ、モーダルを閉じる処理を入れます。
Modal (モーダル) · Bootstrap v5.0

stateについて

この detailModal.hide() によって、モーダルを閉じると、
「1.モーダルを閉じる場合」で説明した、hide.bs.modal のイベントが再び実行されてしまいます。

そうすると、 history.back() が実行されてしまい、2つ前のURLに戻ってしまいます。

これを避けるために、 pushState の第一引数にモーダル表示の際に追加したことを明示する情報をセットしていました。
この第一引数であるstateは、 popstate 時に history.state にて参照することができます。 つまり、「1.モーダルを閉じる場合」にこの情報を見て、history.back() を実行するということができます。

(以下のifのところ)

1
  if (window.history.state === MODAL_STATE) {

ということで

stateを適切に扱うことで、モーダルを閉じた場合と、ブラウザの戻るボタンを押した場合の判断を行なうことができました。

これにて完成です。


See Also

updatedupdated2021-10-142021-10-14
コメントを読み込みますか?