ヘッダーの高さを勘で書かない:ResizeObserver で実測して戻す

ナビの折り返し、遅れて届く Web フォント、内容の長さの変化で、CSS に書いた定数はすぐずれる。ResizeObserver で要素そのものを観測し、実測値を変数に書き戻す。

ヘッダーの高さは? ナビは折り返す? 目次はどれくらいの幅? どれも CSS に大体の値は書けますが、正確には書けません。しかも値は動きます。フォントの読み込み、ウィンドウのリサイズ、内容の長さの変化です。

window の resize では足りない

window.addEventListener('resize') が拾えるのはビューポートのサイズという一種類の変化だけです。フォント到着後の行高の変化、details の展開、同じリサイズ中に非同期で入ってくる内容は抜け落ちます。さらにドラッグ中は毎フレーム発火し、そのハンドラで getBoundingClientRect() を読むとスタイル書き込みと交互になり、同期レイアウトを強制します。

要素そのものを観測する

ResizeObserver が観測するのは要素です。理由を問わず、寸法が変わればコールバックが来ます。

const header = document.querySelector('[data-site-header]');
if (header) {
  const observer = new ResizeObserver(() => {
    const height = Math.round(header.getBoundingClientRect().height);
    document.documentElement.style.setProperty('--anchor-offset', height + 16 + 'px');
  });
  observer.observe(header);
}

コールバックは書き込むだけで、他の要素は読みません。観測対象の寸法に影響する変更を書き込むと、ブラウザは ResizeObserver loop completed with undelivered notifications を投げます。これは「ループを作った」というプラットフォームからの通知です。

実務的な 2 点

  • 書き込む前に丸める。 サブピクセルの高さはコールバックごとに揺れ、--anchor-offset が毎秒何十回も変わり、アンカーの補正まで揺れます。整数化してから、同じ値のうちは書きません。
  • 未対応ブラウザは単に飛ばす。 ここでの用途はすべて補正で、無くてもページは動きます。ポリフィルも、欠落時のエラー通知も入れません。

使わない方がよい場面

CSS で表せる寸法は CSS で書きます。height: auto、aspect-ratio、clamp()、グリッドの自動配置があります。JavaScript で測って書き戻すのは、CSS が既にやっていることを模倣し、レイアウトの往復を 1 回増やすだけです。ResizeObserver の居場所はCSS では表せない要素間の関係です。ヘッダーの高さがアンカーのオフセットを決め、内容の幅がレール型の目次を出すかどうかを決めます。

測れるなら勘で書かない。CSS が計算できるなら測らない。

← 記事一覧に戻る

コメント

…