【React】Swiperのナビゲーションとページネーションをスライダーの外に出す方法

スタッフブログ

こんにちは、マークアップエンジニアの松本です。

Reactでサイトを作っている時、スライダーを実装するために「Swiper」を使用しました。
SwiperはjQuery不要のスライダープラグインで、Reactでも簡単に導入できます。

Swiperで作成したスライダーは、デフォルトではナビゲーションとページネーションがスライドの上に重なるように表示されます。
そのため、ナビゲーションとページネーションをスライドの上に重ねたくない場合は、手を加えてそれらをスライダーの外に出す必要があります。

当時作っていたサイトでもナビゲーションとページネーションをスライダーの外に出す必要があったのですが、どうすれば外に出せるのかが分からず頭を悩ませていました。

今回は、Swiperでスライダーを作る際に困ったナビゲーションとページネーションをスライダーの外に出す方法を、失敗例と共にご紹介します。

※本記事では、スライダーを前後にスライドさせる矢印のことをナビゲーション、画面下部のドットのことをページネーションと表記しています。

Swiperの基本的な使い方

インストール

まずは下記のコマンドでSwiperをインストールします。

npm i swiper

スライダーを作る

基本となるスライダーを作ります。公式のドキュメントに使い方が記載されているので、そこに載っているコードを参考に書いていきます。

import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css';

const Slider = () => {
    return (
        <Swiper>
            <SwiperSlide><img src="http://placehold.jp/700x400.png?text=1" alt=""/></SwiperSlide>
            <SwiperSlide><img src="http://placehold.jp/700x400.png?text=2" alt=""/></SwiperSlide>
            <SwiperSlide><img src="http://placehold.jp/700x400.png?text=3" alt=""/></SwiperSlide>
            <SwiperSlide><img src="http://placehold.jp/700x400.png?text=4" alt=""/></SwiperSlide>
        </Swiper>
    );
};

export default Slider;

<Swiper>がスライダー全体を囲むコンテナで、<SwiperSlide>が各スライドとなる要素です。
今回は画像が流れるスライダーにしたいので、<SwiperSlide>の中身は画像にしました。

ナビゲーションとページネーションを追加する

次に、ナビゲーションとページネーションを追加します。
モジュールとスタイルをインポートして、<Swiper>にpropsとして渡します。

import { Navigation, Pagination } from 'swiper'; // モジュールをインポート
import { Swiper, SwiperSlide } from 'swiper/react';

import 'swiper/css';
import 'swiper/css/navigation'; // スタイルをインポート
import 'swiper/css/pagination'; // スタイルをインポート

const Slider = () => {
    return (
        <Swiper
            // propsとして渡す
            modules={[Navigation, Pagination]}
            navigation
            pagination={{ clickable: true }}
        >
            ~省略~
        </Swiper>
    );
};

export default Slider;

これでナビゲーションとページネーションがついたスライダーの完成です。
出来上がったものがこちら。

これだけでスライダーを作ることが出来ました。

ブラウザの検証ツールを使い、出力されたHTMLの構造を見てみます。

<div class="swiper">

    <div class="swiper-wrapper">
        <div class="swiper-slide"><img src="http://placehold.jp/700x400.png?text=1" alt=""/></div>
        <div class="swiper-slide"><img src="http://placehold.jp/700x400.png?text=2" alt=""/></div>
        <div class="swiper-slide"><img src="http://placehold.jp/700x400.png?text=3" alt=""/></div>
        <div class="swiper-slide"><img src="http://placehold.jp/700x400.png?text=4" alt=""/></div>
    </div>

    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>

    <div class="swiper-pagination">
        <span class="swiper-pagination-bullet"></span>
        <span class="swiper-pagination-bullet"></span>
        <span class="swiper-pagination-bullet"></span>
        <span class="swiper-pagination-bullet"></span>
    </div>

</div>

※わかりやすくするため、クラス名などをいくつか省いています。

全体を囲むSwiperコンポーネント.swiperの直下に、全てのスライドを囲む.swiper-wrapperと、ナビゲーション.swiper-button-prev.swiper-button-next、そしてページネーション.swiper-paginationがあります。

この構造を踏まえて、ナビゲーションとページネーションをスライダーの外に出す方法を考えていきます。

ナビゲーションを外に出す

ナビゲーションをスライダーの外に出したいと思います。

試したこと

まずは自分が思いついた方法を試してみます。

CSSで位置を動かす

ブラウザの検証ツールで見てみると、戻るナビゲーション.swiper-button-prevにはleft: 10px;が適用されていて、進むナビゲーション.swiper-button-nextにはright: 10px;が適用されていました。

どちらもposition: absolute;で絶対配置になっており、左右の位置はleftrightで決めているようなので値を変えて動かしてみます。

コード:

.swiper-button-prev {
  left: -15px !important;
}

.swiper-button-next {
  right: -15px !important;
}

結果:

スライダーの外には出ず、見切れてしまいました。

Swiperコンポーネント.swiperoverflow: hidden;が適用されているため、スライダーの領域外に出そうとすると見切れてしまうようです。
見切れないようにoverflow: visible;で上書きすると、ナビゲーションは見えるようになりますが、見えてほしくない次のスライドが見えてしまいます。

この方法ではうまくいきません。

div要素を作って、デフォルトのクラスをつけてみる

Swiperコンポーネントの外にナビゲーションを作ってみます。
navigationの指定によって生成されたHTMLと同じ構造でdiv要素を作成して、Swiperコンポーネントの外に移動させます。

コード:

<>
    <Swiper
        modules={[Navigation, Pagination]}
        navigation
        pagination={{ clickable: true }}
    >
        ~省略~
    </Swiper>

    {/* 要素を作成 */}
    <div className="swiper-button-prev"></div>
    <div className="swiper-button-next"></div>
</>

結果:

ナビゲーションがスライダーの外に表示されましたが、押しても動作しません。また、デフォルトのナビゲーションが残っているので二重に表示されてしまいます。

この方法でもやはりうまくいきません。

解決策

公式のドキュメントでナビゲーションについて見てみると、prevElnextEl というパラメータがありました。
このパラメータにCSSセレクタまたはHTML要素を指定すると、指定したものと一致する要素がナビゲーションとして動作するようです。

navigation={{
    prevEl: "{CSSセレクタまたはHTML要素}",
    nextEl: "{CSSセレクタまたはHTML要素}"
}}

prevElが前のスライドに戻るナビゲーションで、nextElが次のスライドへ進むナビゲーションです。
早速試してみます。

コード:

<div className="slider__wrapper">

    <Swiper
        modules={[Navigation, Pagination]}
        navigation={{
            // パラメータを設定
            prevEl: "#button_prev",
            nextEl: "#button_next"
        }}
        pagination={{ clickable: true }}
    >
        ~省略~
    </Swiper>

    {/* 要素を作成 */}
    <div id="button_prev" className="swiper-button-prev"></div>
    <div id="button_next" className="swiper-button-next"></div>

</div>
.slider__wrapper {
  position: relative;
}

結果:

今回はbutton_prevbutton_nextというIDを指定しました。指定したIDを持った要素を作成すれば、その要素がナビゲーションとして動作します。
ナビゲーションの見た目はそのままにしたかったので、デフォルトのクラス名をつけました。

position調整のためにSwiperコンポーネントとナビゲーションを囲む要素.slider__wrapperを作り、CSSで見た目や位置を整えたら完成です。

これでナビゲーションをスライダーの外に出すことが出来ました。

ページネーションを外に出す

次はページネーションをスライダーの外に出したいと思います。

試したこと

CSSで位置を動かす

ブラウザの検証ツールを使って見てみると、ページネーション.swiper-paginationposition: absolute;が適用されていたので、position: relative;で上書きしてみます。

コード:

.swiper-pagination {
    position: relative !important;
}

結果:

スライダーの外にページネーションが表示されました。

一見これで問題ないように見えるのですが、スライダーに枠線を付けたい場合に問題が発生します。
Swiperコンポーネント.swiperborderを指定して枠線をつけてみます。

コード:

.swiper {
    border: solid 3px #000 !important;
}

結果:

すると、枠線の中にページネーションが入ってしまいました。

Swiperコンポーネントではなく、全てのスライドを囲む要素.swiper-wrapperに枠線を追加しても、要素がはみ出てしまう上に、スライドを送ると枠線も一緒に移動してしまいます。

スライダーに枠線をつけるなどカスタマイズをしたいときは、この方法ではうまくいきません。

解決策

公式のドキュメントでページネーションについて見てみると、elというパラメータがありました。
elにCSSセレクタまたはHTML要素を指定すると、指定した要素がページネーションとして動作するようです。

pagination={{
    el: "{CSSセレクタまたはHTML要素}"
}}

早速試してみます。

コード:

<div className="slider__wrapper">

    <Swiper
        modules={[Navigation, Pagination]}
        navigation={{
            prevEl: "#button_prev",
            nextEl: "#button_next"
        }}
        pagination={{
            // パラメータを設定
            el: "#pagination"
        }}
    >
        ~省略~
    </Swiper>

    <div id="button_prev" className="swiper-button-prev"></div>
    <div id="button_next" className="swiper-button-next"></div>

    {/* 要素を作成 */}
    <div id="pagination" className="swiper-pagination"></div>

</div>
.swiper {
    border: solid 3px #000 !important;
}

.swiper-pagination {
    position: relative !important;
    margin-top: 20px !important;
}

結果:

今回はpaginationというIDを指定しました。指定したIDを持った要素を作成すれば、その要素がページネーションとして動作します。
見た目はそのままにしたかったので、デフォルトのクラス名をつけました。

.swiper-paginationposition: relative;を指定し、margin等でお好みの位置に調整すれば完成です。
Swiperコンポーネントに枠線をつけた時も、ページネーションはちゃんと枠線の外に表示されています。

これでページネーションをスライダーの外に出すことが出来ました。

まとめ

複雑なことをしなくても、簡単にナビゲーションとページネーションをスライダーの外に出すことが出来ました。

当時は検索して出てきたそれっぽい記事を見漁ったり、思いついた方法を試しては失敗を繰り返して時間を使ってしまいました。
しかし結局は、ドキュメントを見ればすぐに解決することでした。

無闇にトライアンドエラーを繰り返すのではなく、まずはドキュメントを見ることが大事なのだなと痛感しました。