JavaScriptでのメディアクエリ、matchMedia()の使い方を解説
Post on:2020年9月15日
メディアクエリと言えば、CSSの@mediaでスクリーンサイズに合わせて最適なスタイルを適用するものを思い浮かべる人が多いと思います。JavaScriptで実装したコンポーネント、例えば、スライダーなどをレスポンシブ対応にする際にはJavaScriptでメディアクエリを扱った方が便利です。
JavaScriptでのメディアクエリ、matchMedia()の使い方を紹介します。
Working with JavaScript Media Queries
by Marko Ilic
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
メディアクエリと言われて最初に思い浮かぶのは何ですか?
おそらく、下記のようなCSSでしょう。
1 2 3 4 5 6 7 8 9 |
body { background-color: plum; } @media (min-width: 768px) { body { background-color: tomato; } } |
CSSのメディアクエリは、レスポンシブデザインの中心的な存在です。ビューポートのサイズ、モーション、カラー、インタラクション、さらにはプリンターやテレビなどの特定デバイスに基づいているかどうかに関わらず、さまざまなコンテキストに適したスタイルを適用するための優れた方法です。
しかし、JavaScriptにもメディアクエリがあることを知っていましたか?
本当です。JavaScriptのメディアクエリはあまり目にすることはないかもしれませんが、スライダーなど、レスポンシブ対応のプラグインを作成する際に役立つ使用例があることは間違いありません。例えば、特定の解像度でスライダーのアイテムを再描画して再計算する必要があるかもしれません。
JavaScriptでメディアクエリを扱うのは、CSSでメディアクエリを扱うのとは非常に異なります。ただし、コンセプトは似ていますが、いくつかの条件に一致し、いつかのものを適用します。
matchMedia()の使い方
ドキュメントがJavaScriptのメディアクエリ文字列と一致するかどうかを判断するには、matchMedia()メソッドを使用します。これは公式ではWorking Draftの状態にあるCSS Object Model View Module仕様の一部ですが、ブラウザのサポートはIE10を含む98.2%の普及率です。
使い方はCSSのメディアクエリとほぼ同じです。
メディアクエリ文字列をmatchMedia()に渡して、.matchesプロパティでチェックします。
1 2 |
// クエリを定義 const mediaQuery = window.matchMedia('(min-width: 768px)') |
定義されたメディアクエリは、MediaQueryListオブジェクトを返します。これはメディアクエリに関する情報を格納するオブジェクトで、必要なキープロパティは.matchesです。これはドキュメントがメディアクエリと一致する場合にtrueを返す読み取り専用のブール型(Boolean)プロパティです。
1 2 3 4 5 6 7 8 |
// 幅が768px以上のビューポートをターゲットとするメディアクエリを作成 const mediaQuery = window.matchMedia('(min-width: 768px)') // メディアクエリがtrueかどうかをチェック if (mediaQuery.matches) { // その後、alertを実行 alert('Media Query Matched!') } |
これがJavaScriptでメディア条件を一致させるための基本的な使用方法です。オブジェクト(MediaQueryList)を返す一致条件(matchMedia())を作成し、それをチェック(.matches)して、条件がtrueとなった場合に処理を実行します。
CSSとほとんど同じですね!
しかし、それだけではありません。
例えば、ウインドウのサイズが目標とするサイズよりも小さくアップデートされた場合、CSSは何も変更されません。これは.matchesは1回限りの瞬間的なチェックには最適ですが、継続的なチェックをすることができないからです。
つまり、次のことが必要になります。
条件の変化を継続的にチェックする方法
MediaQueryListには、メディアクエリのステータスが変化した時に呼び出されるコールバック関数(.onchangeイベントで表される)を受け入れるaddListener()メソッド(およびその後に続くremoveListener()メソッド)があります。
つまり、条件がアップデートされた時に追加の関数を実行して、アップデートされた条件にレスポンスすることがでできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 幅が768px以上のビューポートをターゲットとする条件を作成 const mediaQuery = window.matchMedia('(min-width: 768px)') function handleTabletChange(e) { // メディアクエリがtrueかどうかを確認 if (e.matches) { // 次に、メッセージをコンソールに記録 console.log('Media Query Matched!') } } // イベントリスナーを登録 mediaQuery.addListener(handleTabletChange) // 初期チェック handleTabletChange(mediaQuery) |
matchMedia()とMediaQueryListのワンツーパンチにより、CSSが提供するメディア条件に一致するだけでなく、アップデートされた条件にも積極的に対応できます。
addListener()でイベントリスナーを登録しても最初は起動しません。イベントハンドラー関数を手動で呼び出し、引数でメディアクエリを渡す必要があります。
See the Pen
JavaScript Media Queries #Example by Maverick (@rema)
on CodePen.
昔ながらの方法
懐かしさのために昔ながらの方法、しかし今でも人気のある方法、JavaScriptで「メディアクエリ」を実行する方法を説明します(このカギ括弧は重要です)。最も一般的なアプローチは、window.innerWidthまたはwindow.innerHeightをチェックするリサイズのイベントリスナーをバインドすることです。
例えば、下記のようなコードです。
1 2 3 4 5 6 7 8 9 10 |
function checkMediaQuery() { // ウィンドウの内側の幅が768pxより大きい場合 if (window.innerWidth > 768) { // 次に、メッセージをコンソールに記録 console.log('Media Query Matched!') } } // ウィンドウのサイズが変更されたときのリスナーを追加 window.addEventListener('resize', checkMediaQuery); |
ブラウザのサイズが変更されるたびにリサイズのイベントが呼び出されるので、これは非常に負荷が高い操作です。空のページでパフォーマンスを調べると、その違いが分かります。
スクリプトが157%増加してる!
もっと簡単な方法は、コンソールログで違いを見ることです。
208個のresizeイベントと6個のmatchMediaイベント
パフォーマンスの問題に目をつむっても、印刷や向きなどの高度なメディアクエリを記述できないという点で、resizeには限界があります。つまり、ビューポートの幅を一致させることで「メディアクエリ」の動作を真似ることはできますが、それ以外の要素と一致させることはできません。そして、真のメディアクエリはそれ以上のことをできることが分かっています。
まとめ
ここまで、JavaScriptでのメディアクエリについて見てきました。matchMedia()でメディア条件を定義する方法を探り、その条件に対して1回限り(.matches)と永続的(addListener())にチェックすることができるMediaQueryListオブジェクトを調べ、関数を呼び出すことで変更(.onchange)にレスポンスできるようにしました。
また、ウインドウのリサイズイベントを調べる「古い」方法も見ました。これはまだ広く使用されており、window.innerWidthのサイズ変更に対応するための合法的な方法ですが、高度なメディア条件のチェックを行うことはできません。
最後に、古い方法では実現できない便利な方法を紹介します。
メディアクエリを使用して、ユーザーがランドスケープモードにいるかどうかをチェックします。この方法はHTML5でゲームを開発するときによく見られるもので、デモはスマホで表示するのが最適です。
See the Pen
JavaScript Screen Orientation Query by Maverick (@rema)
on CodePen.
sponsors