レスポンシブ対応は、現代のウェブ開発において必須の技術です。
基本は 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.matches が true なら、そのメディアクエリが現在の画面サイズに適用されています。
状態の変化を監視する
レスポンシブデザインの多くは、ウィンドウサイズが動的に変化します。
その変化を検知して処理をしたい場合は、監視イベントを登録します。
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では、モジュール化して管理するのが有効です。