LoginSignup
28
17

More than 1 year has passed since last update.

JavaScriptでもメディアクエリが使えるwindow.matchMediaを関数化して使い回す

Posted at

要約

  • window.matchMediaはメディアクエリ文字列を渡して、その条件が切り替わった時にだけ処理を実行してくれます
  • いわゆるdebounce()などでresizeイベントの間引きをする必要がありません(画面の横幅を使う処理はresizeイベントが引き続き適切です)
  • IE10からサポートされているので、ブラウザの対応範囲を気にする必要はありません(matchMedia | Can I use... Support tables for HTML5, CSS3, etc
  • コールバックを実行する関数にして、1箇所でメディアクエリ文字列を管理できます

ソースコード

/**
 * `em`に変換するための基準になるフォントサイズ。
 * @type {number}
 */
const BROWSER_DEFAULT_FONT_SIZE = 16;

/**
 * メディアクエリ変数。キーと値はCSSの変数と合わせます。
 * @type {object}
 */
const list = {
  sm: 375,
  md: 768,
  lg: 1024,
  xl: 1440,
};

/**
 * メディアクエリ変数を管理して、`this.matches(query, layoutChangedCallback)`で`query`が一致するか判定します。
 * @example
 * import MediaQuery from './lib/MediaQuery';
 */
export default class MediaQuery {
  /**
   * メディアクエリがロードで判定またはリサイズで判定が切り替わったときに、コールバックで真偽値を返します。
   * @link https://github.com/yuheiy/real-world-website-boilerplate/blob/master/src/js/mediaQuery.js
   * @param {(String | Number)} query - メディアクエリ文字列(`list`のキー)か数値
   * @param {boolean} layoutChangedCallback - メディアクエリが一致するかの真偽値
   * @example
   * MediaQuery.matches('md', matches => {
   *   console.log(matches ? 'md' : 'sm');
   * });
   * MediaQuery.matches(768, matches => {
   *   console.log(matches ? '768以上' : '767以下');
   * });
   */
  static matches(query, layoutChangedCallback) {
    let mediaQuery = `(min-width: ${list[query] / BROWSER_DEFAULT_FONT_SIZE}em)`;

    if (typeof query === 'number') {
      mediaQuery = `(min-width: ${query / BROWSER_DEFAULT_FONT_SIZE}em)`;
    }

    const mql = window.matchMedia(mediaQuery);
    const listener = event => {
      layoutChangedCallback(event.matches);
    };
    mql.addListener(listener);
    layoutChangedCallback(mql.matches);
    const uninstall = () => {
      mql.removeListener(listener);
    };
    return uninstall;
  }
}

使い方

メディアクエリ文字列(横幅のみ)をlist変数に設定します。

/**
 * メディアクエリ変数。キーと値はCSSの変数と合わせます。
 * @type {object}
 */
const list = {
  sm: 375,
  md: 768,
  lg: 1024,
  xl: 1440,
};

メディアクエリの条件はmin-widthです。

    let mediaQuery = `(min-width: ${list[query] / BROWSER_DEFAULT_FONT_SIZE}em)`;

    if (typeof query === 'number') {
      mediaQuery = `(min-width: ${query / BROWSER_DEFAULT_FONT_SIZE}em)`;
    }

「Sass MQ」のようなメディアクエリのライブラリを使っていたり、自作のメディアクエリmixinを使っている場合でも、以下のようにブレイクポイント変数を使っていると思うのでそれに合わせてください。

$breakpoints: (
  'sm': 'screen and (min-width: 375px)',
  'md': 'screen and (min-width: 768px)',
  'lg': 'screen and (min-width: 1024px)',
  'xl': 'screen and (min-width: 1140px)',
) !default;

md(768px以上)になったらfunc()を実行する例です。
静的メソッドにしているので、new MediaQuery();のようにインスタンスの生成は必要ありません。

import MediaQuery from './lib/MediaQuery';

MediaQuery.matches('md', matches => {
  if (matches) {
    func();
  }
});

mdなどのキーだけでなく横幅(px)を直接指定しても動作します。

import MediaQuery from './lib/MediaQuery';

MediaQuery.matches(768, matches => {
  if (matches) {
    func();
  }
});
28
17
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
17