Swiper × モーダルの複数設置

「コンテンツ(ボタンや画像など)を押したら、モーダルで表示されてスライダー可能」というページの実装ができるようになる記事です。
参考にさせていただいた記事はこちら

それを元にカスタムしたコードがこちら

See the Pen スワイパー×モーダル by てる | Web制作エンジニア (@teruaki_tankawa) on CodePen.

この記事ではSwiperの基本的な使い方は省略します。
基本的な使い方を先に確認したい方はこちら

コードを順番に説明していきます。

それではHTMLです↓

            <div class="modal-front__contents">
                <div class="modal-front__content js-modal-open01" data-modal="1">フロント1-1</div>
                <div class="modal-front__content js-modal-open01" data-modal="2">フロント1-2</div>
                <div class="modal-front__content js-modal-open01" data-modal="3">フロント1-3</div>
            </div>

.modal-front__contents = スタイル用のクラス
.modal-front__content = スタイル用のクラス
.js-modal-open01 = クリックしたらjsで開くためのclass名。スライダー複数設置の時にclass名を分けておいた方がいいので、01としています。(一つだけ設置する場合は01は不要です)
data-modal=”1〜3″ = スライダー表示用のdata-modal属性です。1枚目は1, 2枚目は2 というように足していきます。

            <div class="modal" id="modal">
                <div class="modal__background js-modal-close"></div>
                <div class="modal__contents">
                    <div class="modal__inner">
                        <div class="modal__close-btn js-modal-close" aria-label="閉じる"><span class="modal__cross-line"></span></div>
                        <!-- スライダー -->
                        <div class="swiper swiper-container slider1">
                            <div class="swiper-wrapper">
                                <div class="swiper-slide">
                                    <div class="swiper-slide__body">中身1-1</div>
                                </div>
                                <div class="swiper-slide">
                                    <p class="swiper-slide__body">中身1-2</p>
                                </div>
                                <div class="swiper-slide">
                                    <p class="swiper-slide__body">中身1-3</p>
                                </div>
                            </div>
                            <div class="swiper-button-prev"></div>
                            <div class="swiper-button-next"></div>
                        </div>
                    </div>
                </div>
            </div>

.modal = スタイル用のクラス
#modal = jsでモーダルを扱う用のid
.modal__background,
.modal__contents,
.modal__inner,
.modal__close-btn,
.slider1 = スタイル用のクラス
.js-modal-close = jsでモーダルを閉じる用のid

※重要
以下の構造は崩さないようにしてください。.swiperと.swiper-containerを並列に記載しないとうまく動きません。
swiper, swiper-container > swiper-wrapper > swiper-slide

次はSCSSです↓

// 最初から表示されているコンテンツたち
.modal-front {
    &__contents {
        display: flex;
        flex-wrap: wrap;
        gap: 20px;
        justify-content: space-around;
    }
    &__content {
        padding: 20px 40px;
        margin: 0 0 20px;
        border: 2px solid gray;
        &:hover {
            cursor: pointer;
        }
    }
}

// モーダルまわりのスタイルたち
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    padding: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.3s;
    pointer-events: none;
    opacity: 0;
    z-index: 100;
    background-color: rgba(255, 255, 255, 0.9);
    /* モーダルがactiveの時 */
    &.is-active {
        opacity: 1;
        pointer-events: auto;
    }
    /* モーダル背景 */
    &__background {
        position: absolute;
        width: 100%;
        height: 100%;
        cursor: pointer;
    }
    &__contents {
        position: relative;
        width: 100%;
        max-width: 800px;
        padding: 20px;
    }
    &__inner {
        filter: drop-shadow(0px 0px 4px #ddd);
        background: #FFF;
        width: 90%;
        margin: 0 auto;
        border-radius: 2px;
        padding: 20px 25px;
        display: flex;
        justify-content: flex-start;
        align-items: center;
    }
    &__close-btn {
        position: absolute;
        right: 0;
        top: -40px;
        width: 40px;
        height: 40px;
        cursor: pointer;
        z-index: 20;
        &:hover {
            opacity: 0.8;
        }
    }
    &__cross-line {
        display: inline-block;
        vertical-align: middle;
        color: #313131;
        line-height: 1;
        width: 2rem;
        height: 0.1rem;
        background: currentColor;
        border-radius: 0.1rem;
        position: relative;
        transform: rotate(45deg);
        &::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: inherit;
            border-radius: inherit;
            transform: rotate(90deg);
        }
    }
}

// slider1の下にあるsliderのスタイル
.slider1 {
    position: relative;
    &.swiper-container {
        height: 100px;
    }
    .swiper-wrapper {

    }
    .swiper-slide {
        &__body {
            text-align: center;
        }
    }
}

.modal-front__○○ = 押したらモーダルが発動するトリガーとなるコンテンツたち
.modal__○○ = モーダル系のスタイルたち
.slider1以下 = 「slider1の中の」swiper-containerやswiper-wrapperたちです。別のユニーククラス(例:.slider1-container, .slider1-wrapperなど)をつけてスタイルを当てようとしたら崩れてしまったので、「slider1の下の○○」という書き方をしています。

最後にjsです↓

    const swiper = new Swiper(".swiper", {
        loop: true,
        navigation: {
            nextEl: ".swiper-button-next",
            prevEl: ".swiper-button-prev",
        },
        spaceBetween: 30, //任意のマージン
    });

    /* ------------------ モーダル1ここから ------------------ */
    // モーダルを取得
    const modal = document.getElementById("modal");
    // モーダルを開く
    const openModalBtns = document.querySelectorAll(".js-modal-open01");
    // モーダルを閉じる
    const closeModalBtns = document.querySelectorAll(".js-modal-close");

    // モーダルのボタンクリック
    openModalBtns.forEach((openModalBtn) => {
        openModalBtn.addEventListener("click", () => {
        // data-modalで設定したスライド番号を取得
        const modalIndex = openModalBtn.getAttribute('data-modal');
        swiper[0].slideTo(modalIndex);
        modal.classList.add("is-active");
        });
    });

    // モーダルの閉じるボタンクリック
    closeModalBtns.forEach((closeModalBtn) => {
        closeModalBtn.addEventListener("click", () => {
        modal.classList.remove("is-active");
        });
    });

1〜8行目:.swiperに対してswiperするよ、という宣言です。オプションもここで指定しましょう。
オプションはここでは説明しませんが、色々とあるので調べてみてください。
「スライダーを複数設置するから必要なオプションはそれぞれ違うぞ?」となったアナタは、ひとまずそのプロジェクトで必要なオプションを全部指定しちゃいましょう。
その上で、HTMLタグ(例:.swiper-button-prev)を表示したくないswiperには書かないとか、SCSSでdisplay:none;で消すなどしたら良いかと思います。

それ以外の役割は、ある程度コードの上に日本語で書いているのですが以下に補足を記載します。
.js-modal-open01 = スライダーごとにクラス名を連番もしくは別名にした方が良いかと思います。
例:一つ目のスライダーは.js-modal-open01, 二つ目のスライダーは.js-modal-open02など
理由は、クラス名を一緒にすると別スライダーのコンテンツがチラつく可能性があるからです。

※重要
swiper[0].slideTo(modalIndex)の行 = swiperオブジェクトのindex番号を指定する必要があります。
例:プロジェクト内に二つswiperを設置する際には、swiperのindexの0と1がつくられます。
そのため、一つ目は0、二つ目は1で指定してあげる必要があります。(二つ目は後ほど説明します)

これで一つ目のswiperは完成です!(閉じるボタンの説明は省略します)

二つ目のモーダルの書き方を説明します。
まずはHTMLから↓

    <div class="modal-swiper">
        <div class="modal-front__contents">
            <div class="modal-front__content js-modal-open02" data-modal="4">フロント2-1</div>
            <div class="modal-front__content js-modal-open02" data-modal="5">フロント2-2</div>
            <div class="modal-front__content js-modal-open02" data-modal="6">フロント2-3</div>
        </div>
    
        <div class="modal" id="modal02">
            <div class="modal__background js-modal-close02"></div>
            <div class="modal__contents">
                <div class="modal__inner">
                    <div class="modal__close-btn js-modal-close" aria-label="閉じる"><span class="modal__cross-line"></span></div>
                    <div class="swiper swiper-container slider2">
                        <div class="swiper-wrapper">
                            <div class="swiper-slide">
                                <div class="swiper-slide__body">中身2-1</div>
                            </div>
                            <div class="swiper-slide">
                                <div class="swiper-slide__body">中身2-2</div>
                            </div>
                            <div class="swiper-slide">
                                <div class="swiper-slide__body">中身2-3</div>
                            </div>
                        </div>
                        <div class="swiper-button-prev"></div>
                        <div class="swiper-button-next"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>

ほぼ一緒です。二つ目なので、「○○02」となっている箇所はそれぞれ修正してください。

※重要
data-modal=”4″から始まっていることに注目してください。
一つ目のswiperで指定した最後の数の次から指定して下さい。
例:一つ目のswiperでdata-modal=”3″まで指定した場合、二つ目のswiperのdata-modal=”4″にする

お次はSCSS↓

// slider2の下にあるsliderのスタイル
.slider2 {
    position: relative;
    &.swiper-container {
        height: 100px;
    }
    .swiper-wrapper {

    }
    .swiper-slide {
        &__body {
            text-align: center;
        }
    }
}

最後にjs↓

const modal02 = document.getElementById("modal02");
    const openModalBtns02 = document.querySelectorAll(".js-modal-open02");
    const closeModalBtns02 = document.querySelectorAll(".js-modal-close");

    openModalBtns02.forEach((openModalBtn02) => {
        openModalBtn02.addEventListener("click", () => {
            const modalIndex = openModalBtn02.getAttribute('data-modal'); // getAttribute = 属性の値を返す → 数字を取得する
            const modalIndex2 = modalIndex - 3;
            modal02.classList.add("is-active");
            swiper[1].slideTo(modalIndex2);
        });
    });

    closeModalBtns02.forEach((closeModalBtn02) => { // モーダルの閉じるボタンクリック
        closeModalBtn02.addEventListener("click", () => {
            modal02.classList.remove("is-active");
        });
    });

こちらも、constやループ内で02になっているところが多数あるかと思います。
同じように、二つ目は02とか、secondとかわかりやすい名前で指定してあげてください。

※重要1
.js-modal-open02 = 先ほども説明していた箇所ですが、openクラスが一緒だと別スライダーの中身がチラつく可能性があるため、02としています。

※重要2
const modalIndex2 = modalIndex – 3; = 一つ目のswiperの枚数分を引いてください。
例:一つ目のswiperで3枚スライドがあったら二つ目は「-3」する。
【補足】
data-modal=”○○”は、数字を取得します。
二つ目のswiperの1番目のスライドを表示する場合、data-modalは4を取得しますが、この行の2行下で「swiperオブジェクト2番目の○番目のスライドを表示する」という指示をするので、計算して「1」にしてあげる必要があるからです。

参考↓

※重要3
swiper[1].slideTo(modalIndex2); = 重要2で説明していた箇所です。「swiperのindex番号1の、○番目のスライドを表示」という書き方をしています。

以上です!
もっと簡単な指定方法があるかもなのですが、あったらぜひ教えてください。