window.matchMediaによる、メディアクエリに応じたJavaScriptの実行

# はじめに

レスポンシブデザインのWebページを制作する際、CSSのメディアクエリで指定した条件（ブラウザ幅などのブレークポイント）に応じてJavaScriptの処理を実行させたい場面があります。

ブラウザ幅の変化を扱う場合、古くは「`window` の `resize` イベントを監視してブラウザ幅を取得する」といった手法も取られていましたが、パフォーマンスなどを考慮した場合には少し実装に工夫が必要でした。

IE10以降の主要なブラウザであれば **window.matchMedia** メソッド を利用することでシンプルな実装が可能です。

# window.matchMedia メソッドを利用した実装

例として、ブラウザ幅 `769px` をブレークポイントとする場合を考えると、JavaScriptのコードは次のようになります。

```js:JavaScript
// 1. 変数mqlにMediaQueryListを格納
const mql = window.matchMedia('(min-width: 769px)');

// 2. メディアクエリに応じて実行したい処理を関数として定義
const handleMediaQuery = function(mql) {
  if (mql.matches) {
    // 769px以上の場合の処理
    console.log('769px以上');
  } else {
    // 769px未満の場合の処理
    console.log('769px未満');
  }
};

// 3. イベントリスナーを追加（メディアクエリの条件一致を監視）
mql.addListener(handleMediaQuery);

// 4. 初回チェックのため関数を一度実行
handleMediaQuery(mql);
```

大きく4つの処理に分かれているので、1つずつ内容を追ってみます。

### 1. 変数mqlにMediaQueryListを格納

```js
const mql = window.matchMedia('(min-width: 769px)');

console.log(mql); // MediaQueryList {media: "(min-width: 769px)", matches: true, ...}
```

まず、Webページ上で利用したいメディアクエリを引数として `window.matchMedia` メソッドを実行します。
`window.matchMedia` メソッドの実行により `MediaQueryList` オブジェクトが生成されるので、それを変数 `mql` に格納しています。

`MediaQueryList` オブジェクトは `matches` というプロパティを持っており、メディアクエリに一致した条件下では `true` が、一致しない条件下では `false` が格納されます。

### 2. メディアクエリに応じて実行したい処理を関数として定義

```js
const handleMediaQuery = function(mql) {
  if (mql.matches) {
    // 769px以上の場合の処理
    console.log('769px以上');
  } else {
    // 769px未満の場合の処理
    console.log('769px未満');
  }
};
```

ここでは `handleMediaQuery` という関数を定義し、前述した `matches` プロパティの値（`true` or `false`）によって実行したい処理を書き分けています。

### 3. イベントリスナーを追加（メディアクエリの条件一致を監視）

```js
mql.addListener(handleMediaQuery);
```

`MediaQueryList` オブジェクトの `addListener` メソッドでイベントリスナーを設定できます。
`MediaQueryList.matches` が変化するタイミング（今回の例ではブラウザ幅が769pxを越えるタイミング）で、引数に指定した `handleMediaQuery` 関数が実行されます。

### 4. 初回チェックのため関数を一度実行

```js
handleMediaQuery(mql);
```

Webページの初期状態でメディアクエリに応じた処理をおこなうため、一度 `handleMediaQuery` 関数を実行しています。

ここまでのJavaScriptコードを実行すると、ブラウザ幅に応じてコンソールに `769px以上` / `769px未満` というメッセージが表示されることを確認できるかと思います。


# ブレークポイントの通過をカスタムイベントとして扱う

メディアクエリの条件一致チェックの際に **カスタムイベント** を発生させておくことで、ブレークポイントの通過（例えばPCビューとモバイルビューの切り替え）をWebページ上での一つのイベントとして取り扱うことができます。
カスタムイベントの利用にはjQueryの `trigger` / `on` メソッドが便利です。

[jQueryでカスタムイベントを利用する（.on() と .trigger()）](https://qiita.com/shouchida/items/de326cb608027b46f58c)

```js
const mql = window.matchMedia('(min-width: 769px)');

const handleMediaQuery = function(mql) {
  // カスタムイベント 'breakpoint-{type}' を発火
  if (mql.matches) {
    $(window).trigger('breakpoint-large');
  } else {
    $(window).trigger('breakpoint-small');
  }
};

mql.addListener(handleMediaQuery);
handleMediaQuery(mql);
```

```js
function doSomething() {
  $(window).on('breakpoint-large', function() {
    // 769px以上の場合の処理
    console.log('769px以上');
  });

  $(window).on('breakpoint-small', function() {
    // 769px未満の場合の処理
    console.log('769px未満');
  });
}

doSomething();
```

上記の例では、`doSomething` 関数内でカスタムイベントを利用し、ブラウザ幅に応じた処理を実行しています。

# 参考リンク
[Can I use... matchMedia](https://caniuse.com/#search=matchMedia)
[window.matchMedia - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Window/matchMedia)
[MediaQueryList - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/MediaQueryList)