JavaScriptでメディアクエリ(window.matchMedia)を使う方法

レスポンシブ対応は、現代のウェブ開発において必須の技術です。
基本は CSS のメディアクエリで調整しますが、画面サイズに応じて JavaScript で動きを変えたい 場面もよくあります。
例えば、モバイルではメニューを閉じる、高解像度の画像に切り替える、大画面のときだけスクロール処理を有効にする、などです。

そういった場面で役立つのが、window.matchMedia というブラウザAPIです。
この記事では、基礎から実践例、さらに再利用しやすいモジュール化まで詳しく解説します。

window.matchMediaとは

window.matchMedia は、JavaScriptからメディアクエリの条件を判定したり監視したりするためのメソッドです。
CSS で書く以下のようなメディアクエリと同じ条件を、JavaScriptでも利用できます。

@media (max-width: 768px) { body { background-color: red; }}
CSS

この条件をJavaScriptで確認するには以下のようにします。

const mq = window.matchMedia('(max-width: 768px)');if (mq.matches) { console.log('画面幅は768px以下です');} else { console.log('画面幅は768pxより大きいです');}
JavaScript

このように、mq.matchestrue なら、そのメディアクエリが現在の画面サイズに適用されています。

状態の変化を監視する

レスポンシブデザインの多くは、ウィンドウサイズが動的に変化します。
その変化を検知して処理をしたい場合は、監視イベントを登録します。

const mq = window.matchMedia('(max-width: 768px)');mq.addEventListener('change', (e) => { if (e.matches) { console.log('768px以下になりました'); } else { console.log('768pxを超えました'); }});
JavaScript

このコードは、ウィンドウサイズが768pxを境に変わったときにコールバックが呼ばれます。

よくある利用例

小さい画面ではメニューを閉じる

モバイル画面ではメニューを閉じる、デスクトップでは開いておく、という処理例です。

const mq = window.matchMedia('(max-width: 768px)');function toggleMenu(e) { const menu = document.querySelector('#menu'); if (e.matches) { menu.classList.remove('is-open'); } else { menu.classList.add('is-open'); }}toggleMenu(mq); // 初回実行mq.addEventListener('change', toggleMenu);
JavaScript

大きい画面で高解像度の画像に切り替える

画面が広いときだけ重い画像を読み込む例です。

const mq = window.matchMedia('(min-width: 1024px)');function swapImage(e) { const img = document.querySelector('#hero'); if (e.matches) { img.src = 'high-res.jpg'; } else { img.src = 'mobile-res.jpg'; }}swapImage(mq);mq.addEventListener('change', swapImage);
JavaScript

大きい画面のときだけスクロール処理を有効にする

スクロール監視は処理負荷が高いため、必要な場合だけ有効化する例です。

const mq = window.matchMedia('(min-width: 1024px)');function onScroll() { console.log('スクロール中');}function updateScroll(e) { if (e.matches) { window.addEventListener('scroll', onScroll); } else { window.removeEventListener('scroll', onScroll); }}updateScroll(mq);mq.addEventListener('change', updateScroll);
JavaScript

モジュール化して管理しやすくする

画面幅ごとに複数の処理を登録したいとき、毎回 matchMedia を書くと煩雑になります。
そこで、再利用しやすいモジュールとしてまとめる方法を紹介します。

モジュール例: mediaQueryManager.js

以下は、メディアクエリごとにコールバックを登録・解除できるモジュールです。

const queries = {};export function register(queryString, callback) { if (!queries[queryString]) { const mql = window.matchMedia(queryString); const handler = (e) => callback(e.matches); handler(mql); // 初回呼び出し mql.addEventListener('change', handler); queries[queryString] = { mql, handler }; } else { console.warn(`Query "${queryString}" はすでに登録されています`); }}export function unregister(queryString) { const entry = queries[queryString]; if (entry) { entry.mql.removeEventListener('change', entry.handler); delete queries[queryString]; } else { console.warn(`Query "${queryString}" は登録されていません`); }}
JavaScript

利用例: main.js

import { register, unregister } from './mediaQueryManager.js';register('(max-width: 768px)', (isMobile) => { if (isMobile) { console.log('モバイルモード'); } else { console.log('デスクトップモード'); }});// 解除する場合// unregister('(max-width: 768px)');
JavaScript

状態を取得する関数も追加したい場合

現在の状態を参照する関数もモジュールに含めると便利です。

export function getState(queryString) { const entry = queries[queryString]; if (entry) { return entry.mql.matches; } console.warn(`Query "${queryString}" は登録されていません`); return null;}
JavaScript

使用例:

if (getState('(max-width: 768px)')) { console.log('現在はモバイルモードです');}
JavaScript

補足とポイント

  • 初期状態の確認は必ず行う
    .matches はその時点の状態を返すので、最初に実行して現在の状態に応じて処理する。
  • 監視は .addEventListener('change', …) を使う
    → 古いブラウザでは .addListener が必要だが、現代のブラウザでは不要。
  • モジュール化することで、複数のメディアクエリの登録・解除が簡単になる。

まとめ

window.matchMedia を活用することで、CSS だけでは難しい挙動も柔軟に実現できます。
特に大規模なプロジェクトや複雑なインタラクションを持つUIでは、モジュール化して管理するのが有効です。

read next