【JavaScript】MediaQueryList を使って画面幅を評価しよう!

javascript
この記事は約13分で読めます。

はじめに

2023年現在、いろいろなサイズのスマホが登場していますね。
iPhone で言えば、
SE はこんなコンパクトなのがあるの!?と思う一方で、
Pro はこんなにでかいの?ポケットぎり入るくらいじゃん!!
といったように、スマホといっても様々な画面サイズがあります。

ケン
ケン

自分はでかいの要らないから、めちゃくちゃ小さいスマホが欲しいです。。。
13min 使ってても、画面左上のアイコンが親指工夫しないと届かないのがちょっとヤダ!

クライアントに見せるレイアウトをユーザーエージェントではなく、クライアントが見ている画面幅に適したレイアウトを常に見せたいですね!

クロたん
クロたん

ん?cssで調整すればいい
んじゃにゃい~?

わい
わい

さすが、くろたん!でも、↓のようなケースだとどうだろう?
例えば jsライブラリの1つで、画像などをスライド形式で表示してくれる Swiper を使うとき、スマホでは 1枚ずつスライドさせて、PCでは3枚ずつスライドさせるといったことができないよね!
Swiper の設定自体は HTML / CSS の領域じゃないから、クライアントの画面幅を js で評価して適切な Swiper設定ができるようにしたいというのが、今回のテーマ!

クロたん
クロたん

CSS!!!

わい
わい

ハウス!

ということで…

この記事では、画面幅を js で評価して、その評価値に基づいて Swiperの設定 を変更して、
適切なUIを提供するということを順々に紹介したいと思います!

必要な環境は、HTML / JavaScript ファイル と、ブラウザだけです!

わい
わい

そう、なにもいらない!

MediaQueryList で画面幅を評価する!

// MediaQueryListオブジェクトを生成
const MediaQueryList = window.matchMedia("(max-width: 768px)");

// 最初に画面幅を評価して、現在のクライアントの画面幅が768px以上か以下かを判定する
// 768px以内であれば、true
// 768px以上であれば、false
let mobile_flg = MediaQueryList.matches;

// 画面幅が768pxをまたいだらイベント発火する
MediaQueryList.addEventListener('change', obj => {
    mobile_flg = obj.matches
})

↑たったこれだけです!コメントにある程度書いていますが、補足の意味も込めてポイント解説していきます!

// MediaQueryListオブジェクトを生成
const MediaQueryList = window.matchMedia("(max-width: 768px)");

max-width: 768px に関しては、bootstrap の md のブレイクポイントです!
大雑把にですが、768px以下はスマホと評価して、それ以上は PC / タブレット などで見ているのかなとしています!
正直、デバイスの名称とかはどうでもよくて、クライアントの画面幅が大きいのか小さいのかってことだけわかればいいです!なので、max-width は自身がここらへんだろうと思う幅を指定してください!

// 最初に画面幅を評価して、現在のクライアントの画面幅が768px以上か以下かを判定する
// 768px以内であれば、true
// 768px以上であれば、false
let mobile_flg = MediaQueryList.matches;

読み込み時に、クライアントの画面サイズを評価して、mobile_flg 格納します!

// 画面幅が768pxをまたいだらイベント発火する
MediaQueryList.addEventListener('change', obj => {
    mobile_flg = obj.matches
})

↑ここが MediaQueryList の強みです!
コメント部分の 「768pxをまたいだら」の「またいだら」に注目してください!
つまり、画面幅が変わったらイベントが発火するのではなくて、画面幅が 768px という境界線をまたいだときにのみイベントが発火します。

よくあるやつだと、 resizeイベント を使って以下のように画面幅を評価するものです。

window.addEventListener('resize', () => {
    console.log(window.innerWidth)
})

resizeイベントは、画面幅が変わるたびに毎度イベントが発火しますが、
MediaQueryList は画面幅が変わるだけではイベントは発火しません。

ケン
ケン

画面幅が変わったら、とかさらっと言ったけど、そんなことする人はほとんどおらんw
そんなことするのは、レスポンシブ開発中の開発者だけ説。

クロ
クロ

それ言い出したら、この記事必要ないにゃ~。。。

ケン
ケン

ユースケース募集中です。。。

評価した値を用いて、Swiper の設定を変更しよう!

インストール または、 読み込み

  • Swiper インストール(npm が使えない、または面倒な人はCDNで!)
npm install swiper
  • CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"/>

<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>

↑CDNの場合は、<head>部分に CSS を記述し、<body>の終了タグ直前に script を記述します!
scriptに関しては、head に書きたい場合は、以下のように defer属性 をつけましょう!

<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"/>
    <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js" defer></script>
</head>

HTML / CSS

Swiperを使う上で必須のタグとクラスを↓に記述しています。

  • HTML
<div class="swiper">

    <div class="swiper-wrapper">

        <!-- swiper-slideは画像3枚なので、3つ記述してある -->
        <div class="swiper-slide">
            <img
                src="https://express-it.site/wp-content/uploads/2023/11/%E3%80%90javascript%E3%80%91-MutationObserver-%E3%81%A7DOM%E8%A6%81%E7%B4%A0%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%97%E3%82%88%E3%81%86%EF%BC%81-2-300x157.png">
        </div>
        <div class="swiper-slide">
            <img
                src="https://express-it.site/wp-content/uploads/2023/09/%E3%80%90Laravel-Vue.js%E3%80%91-fontawesome-%E3%82%92%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B%EF%BC%81-300x157.png">
        </div>

        <div class="swiper-slide">
            <img src="https://express-it.site/wp-content/uploads/2023/11/%E3%80%90javascript%E3%80%91-MutationObserver-%E3%81%A7DOM%E8%A6%81%E7%B4%A0%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%97%E3%82%88%E3%81%86%EF%BC%81-300x157.png">
        </div>

    </div>
    <!-- スライダーにスライド回数分出てくる黒ポチ -->
    <div class="swiper-pagination"></div>

    <!-- スライダーの前・次矢印 -->
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>

    <!-- スライダーの現在位置をスクロールバー表示する -->
    <div class="swiper-scrollbar"></div>
</div>
  • CSS
    <style>
        .swiper {
            width: 100%;
            height: 100%;
        }

        .swiper-slide {
            text-align: center;
            font-size: 18px;
            background: #fff;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .swiper-slide img {
            display: block;
            width: 100%;
            object-fit: cover;
        }
    </style>

css に関しては 公式Swiper のドキュメントから引っ張ってきました!

JavaScript

// MediaQueryListオブジェクトを生成
const MediaQueryList = window.matchMedia("(max-width: 768px)");

// 最初に画面幅を評価して、現在のクライアントの画面幅が768px以上か以下かを判定する
// 768px以内であれば、true
// 768px以上であれば、false
let mobile_flg = MediaQueryList.matches;

// 画面幅が768pxをまたいだらイベント発火する
MediaQueryList.addEventListener('change', obj => {
    mobile_flg = obj.matches
})

/** 以下、Swiper の設定 */
// 実務であれば必要なモジュールだけ読み込むのが
// ↑不要なモジュールまで読み込むと、それだけ時間かかるしなにより無駄だからね
import Swiper from 'swiper/bundle';
// cssに関しても↑と同じことが言える!bundleって「束」って意味だから納得
import 'swiper/css/bundle';

document.addEventListener('DOMContentLoaded', () => {
    // swiperの設定
    const config = {
        // 今回のテーマはまさに以下の部分
        // 画面幅が小さい時は、各スライドに表示される画像は1枚で、
        // 画面幅が大きい時は、各スライドに表示される画像は2枚
        slidesPerView: (mobile_flg === true) ? 1 : 2,
        
        // スライドの終了の次は開始の画像(オワリハジマリ)
        loop: true,
        
        // HTMLで記述した pagination をアクティブにする
        pagination: {
            el: '.swiper-pagination',
        },

        // HTMLで記述した navigation をアクティブにする
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },

        // HTMLで記述した scrollbar をアクティブにする
        scrollbar: {
            el: '.swiper-scrollbar',
        },
    }
    // new してスワイパーを起動
    new Swiper('.swiper', config);
})
        // 今回のテーマはまさに以下の部分
        // 画面幅が小さい時は、各スライドに表示される画像は1枚で、
        // 画面幅が大きい時は、各スライドに表示される画像は2枚
        slidesPerView: (mobile_flg === true) ? 1 : 2,
わい
わい

まさにこの部分ですね!

js で評価した画面幅の値をベースにして、各スライドに表示される画像枚数を分岐しています!

これで、クライアントの画面幅に合わせたスライダー設定が可能となりました!

イメージは↓みたいな感じ!

ちなみに…

JavaScript のコメントで実務では云々と書きましたが、必要なモジュールだけ読み込んで使用するには以下のような感じになります!

今回は、Pagination / Navigation / Scrollbar のモジュールを使ったのでそれをもとに記述していきます!

/** 以下、Swiper の設定 */
import Swiper from 'swiper';

// 必要なモジュールだけ読み込む
import { Navigation, Pagination, Scrollbar } from 'swiper/modules';

// 必要な css だけ読み込む
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/scrollbar';

document.addEventListener('DOMContentLoaded', () => {
    // swiperの設定
    const config = {
        // 読み込んだモジュールを使えるようにする
        modules: [Navigation, Pagination, Scrollbar],
        slidesPerView: (mobile_flg === true) ? 1 : 2,
        loop: true,
        pagination: {
            el: '.swiper-pagination',
        },
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },
        scrollbar: {
            el: '.swiper-scrollbar',
        },
    }
    new Swiper('.swiper', config);
})
わい
わい

コメント書いてある部分が変更箇所です!

非常にシンプルです!

さいごに

最近は HTML / CSS / JavaScript 、いわゆるフロントエンドが楽しくなってきて暇あれば調べ物をしています。といっても、99% JavaScript のことを調べていますがw

バックエンドをずっとやってきていたのでフロントは少し敬遠していましたが、ブラウザの仕組みとかフロントエンドでもできることが多くあることに気づき、驚きの連続です。。。

バックエンドが優れていても結局人が使うサービスを作るのであれば、フロントも最善を尽くして使いやすいサービスを開発できればなと思う今日この頃です!

クロ
クロ

がんばるにゃ~~~!

わい
わい

では、また!

コメント