jQuery無しの環境で「matchHeight」が使いたい

# はじめに
jquery.matchHeight.js
使用したことはありますでしょうか？
自分はこれまで何度もお世話になってきました。

https://github.com/liabru/jquery-match-height

しかし昨今、私は下記が気になっていました。
- jqueryに依存しない環境がメジャーになり、使いにくい場面が増えている
- jqueryのresizeイベントを利用していることで、パフォーマンス的にも良くない点がある

# 目標
そこで、この記事では 「jquery.matchHeight.js」 を参考にしつつ、以下の点を達成した、気軽に使用できるコードを提示したいと思います。
- jquery非依存
- matchHeightとして必要な、横並びの要素の高さを揃え、リサイズされた時には高さを再設定する処理
- パフォーマンスを改善
- 機能を絞ってシンプルに


# 完成コード
```javascript: matchHeight.js
export default class MatchHeight {
  constructor(targetClass, observeClass) {
    this.targetClass = targetClass;
    this.targetEls = Array.from(document.querySelectorAll(targetClass));
    this.observeEl = document.querySelector(observeClass);
    this.init();
  }

  _row(elements) {
    const tolerance = 1;
    let lastTop = null;
    let rows = [];

    elements.forEach(el => {
      const top = el.getBoundingClientRect().top;
      const lastRow = rows.length > 0 ? rows[rows.length - 1] : null;

      if (!lastRow || Math.floor(Math.abs(lastTop - top)) > tolerance) {
        rows.push([el]);
      } else {
        lastRow.push(el);
      }

      lastTop = top;
    });

    return rows;
  }

  _reset(els) {
    els.forEach(el => {
      el.style.height = 'auto';
    });
  }

  _match(els) {
    this._reset(els);
    const max_h = Math.max(...els.map(el => el.clientHeight));
    els.forEach(el => {
      el.style.height = max_h + 'px';
    });
  }

  _bind(els) {
    const rows = this._row(els);
    rows.forEach(row => {
      this._match(row);
    });
  }

  _setObserver(targetEls, observeEl) {
    this.observer = new ResizeObserver(() => {
      this._bind(targetEls);
    });
    this.observer.observe(observeEl);
  }

  init() {
    this._setObserver(this.targetEls, this.observeEl);
  }

  update() {
    this.targetEls = Array.from(document.querySelectorAll(this.targetClass));
    this.observer.disconnect();
    this._setObserver(this.targetEls, this.observeEl);
  }
}
```

```javascript: index.js
import MatchHeight from '/matchHeight'; // 任意のディレクトリ

// 使用例
// items: 高さを揃えたい要素
// wrapper: リサイズを検知したい要素（この要素がリサイズすると、高さを揃える処理が走ります）
new MatchHeight('.items', '.wrapper');
```

## 簡単に解説
MatchHeightクラスの中で定義している関数について、以下でそれぞれ簡単に役割を説明します。

### _row()
横並びの要素を配列にまとめる

### _reset()
それぞれの要素の高さを取得するために、一時的にheightプロパティをautoにする

### _match()
_row()でまとめた横並びの要素の中で一番高い要素の高さを _reset()を利用して取得し、横並びの要素それぞれのheightプロパティに指定する

### _bind()
上の３つの関数を利用することで、横並びの要素の高さを揃える

### _setObserver()
ResizeObserverを使用することで、必要なタイミングでのリサイズのみを検知し、_bind()を発火させることで高さを再設定する

引数には以下をを設定
第一引数：高さを揃えたい要素のリスト
第二引数：リサイズを検知したい要素

### _init()
_setObserver()の引数にそれそれ要素を設定して発火させる


### _update()
以下の処理を行って更新します
- 高さを揃えたい要素を再取得
- ResizeObserverでのリサイズ監視を解除
- _setObserver()を呼び出し直す

（高さを揃えたい要素が増えた時などに使用ください）

- - -

> 上記の中で
・_row
・_resize
・_match
３つの関数は「jquery.matchHeight.js」を流用、参考にしております。

- - -

説明の途中で「ResizeObserver」というものが出てきましたね。
簡単に説明すると以下のようなWebAPIです。

#### ResizeObserver
>ResizeObserver は要素の矩形領域のサイズの変化を監視して、そのサイズに変化があるとコールバック関数を呼び出すオブジェクトです。
window.resize イベントではウィンドウサイズ（ビューポート）の変更を検知しますが、ResizeObserver を使用すると特定の要素のサイズ変更を簡単に検出できます。

https://www.webdesignleaves.com/pr/jquery/resizeObserver.html

要は、
resizeイベントがウィンドウのリサイズを全て検知するのに対して、
ResizeObserverは **指定した要素がリサイズした時のみ検知** してくれる
というものになります。

上記のコードでは、パフォーマンスの改善を目的にこのResizeObserverを利用しています。

# 注意点
高さを揃える要素の中に**cssなどでサイズが可変する**プロパティなどがある場合、ResizeObserverでbody要素を指定したり、指定する要素のmax-widthを可変が発生し始めるサイズにするなどの調整が必要な場合があります。

これはResizeObserverは指定した要素にリサイズが発生しない場合は処理が呼び出されないためです。

# まとめ
「完成コードそのままで十分だよ〜」だとありがたいですが、、

完成コードには必要最低限の機能しかありません。
また、TypeScriptでもありません。
上記の注意点もあります。
なので、ご利用される状況に応じて調整・追記など入れてもらえればと思います。

以上、何かのお役に立てれば〜〜〜〜〜