序章

サイズ変更オブザーバーは、 Intersection ObserverAPIなどの他のオブザーバーAPIと非常によく似た新しいJavaScriptAPIです。 サイズが変更されたときに要素に通知することができます。

要素のサイズが変更される最も一般的な理由は、ビューポートのサイズが変更されたとき、またはデバイスの方向が縦向きと横向きの間で変更されたときです。 この時点まで、サイズ変更イベントをリッスンし、特定の要素のサイズが変更されているかどうかを確認するには、グローバルwindow.resizeイベントに依存する必要がありました。 これにより、トリガーされたイベントが大量に発生するため、パフォーマンスの問題が発生しやすくなります。 つまり、window.resizeを使用すると、要素のサイズが実際に変更されたときだけでなく、すべてのビューポートサイズの変更が通知されるため、無駄になることがよくあります。

Resize Observer APIには、ウィンドウのサイズ変更イベントでは役に立たない別のユースケースもあります。要素がDOMに動的に追加または削除され、親要素のサイズに影響を与える場合です。 これは、最近のシングルページアプリでますます頻繁になっています。

このチュートリアルでは、ReactObserverの基本的な使用法について学習します。 また、独自のフロントエンドコードにReact Observerを実装し、ブラウザーのサポートをテストします。

前提条件

このチュートリアルを正常に完了するには、次のものが必要です。

ステップ1—サイズ変更の基本的な使用法を理解する

Resize Observerの使用は、新しいResizeObserverオブジェクトをインスタンス化し、監視されるエントリを受け取るコールバック関数を渡すことによって行われます。

const myObserver = new ResizeObserver(entries => {

});

コールバック関数内で、エントリを反復処理できます。 ResizeObserverがインスタンス化されると、observe関数がインスタンスで呼び出され、監視する要素が渡されます。

const someEl = document.querySelector('.some-element');
const someOtherEl = document.querySelector('.some-other-element');

myObserver.observe(someEl);
myObserver.observe(someOtherEl);

各エントリには、contentRectおよびtargetプロパティを持つオブジェクトが割り当てられます。 targetはDOM要素自体であり、contentRectは、幅、高さ、x、y、上、右、下、左のプロパティを持つオブジェクトです。

要素のgetBoundingClientRectとは異なり、幅と高さのcontentRect値にはパディング値が含まれていません。 contentRect.topは要素の上部のパディングであり、contentRect.leftは要素の左側のパディングです。

たとえば、要素のサイズが変更されたときに観測された要素の幅と高さをログに記録する場合は、myObserverという定数変数を作成し、新しいResizeObserverをインスタンス化することから始めます。

const myObserver = new ResizeObserver(entries => {

});

コールバック関数内で、forEachを使用してすべてのエントリを反復処理します。

const myObserver = new ResizeObserver(entries => {
  entries.forEach(entry => {
  
  });
});

forEachループ内で、console.logは、それぞれentry.contentRect.widthentry.contentRect.heightを使用した各エントリの幅と高さです。

const myObserver = new ResizeObserver(entries => {
  entries.forEach(entry => {
    console.log('width', entry.contentRect.width);
    console.log('height', entry.contentRect.height);
  });
});

myObserverを使用するには、DOMセレクターを使用してsomeElという要素を作成します。 myObserver.observeの引数としてsomeElを渡します。

const myObserver = new ResizeObserver(entries => {
  entries.forEach(entry => {
    console.log('width', entry.contentRect.width);
    console.log('height', entry.contentRect.height);
  });
});

const someEl = document.querySelector('.some-element');
myObserver.observe(someEl);

Resize Observerの使用方法を理解したら、実際のユースケースでResizeObserverを利用することに進むことができます。

ステップ2—サイズ変更オブザーバーの使用方法のデモンストレーション

以下は、ResizeObserverAPIの動作を確認するためのデモです。 ブラウザウィンドウのサイズを変更して試してみて、要素のサイズが実際に影響を受ける場合にのみ、グラデーションの角度とテキストの内容がどのように変化するかを確認してください。

Resize Observer APIを使用する前に、まずindex.htmlファイルを作成する必要があります。

  1. touch index.html

HTMLファイル内に、次のコードを追加します。

index.html
<div class="box">
  <h3 class="info"></h3>
</div>
<div class="box small">
  <h3 class="info"></h3>
</div>

また、HTMLにいくつかのスタイルを追加する必要があります。 styles.cssファイルを作成し、そのファイルに次のCSSコードを追加します。

styles.css
.box {
  text-align: center;
  height: 20vh;
  border-radius: 8px;
  box-shadow: 0 0 4px var(--subtle);

  display: flex;
  justify-content: center;
  align-items: center;
}

.box h3 {
  color: #fff;
  margin: 0;
  font-size: 5vmin;
  text-shadow: 0 0 10px rgba(0,0,0,0.4);
}

.box.small {
  max-width: 550px;
  margin: 1rem auto;
}

.box要素にグラデーションの背景を適用する必要がなかったことに注目してください。 サイズ変更オブザーバーは、ページが最初に読み込まれたときに1回呼び出され、次にグラデーションが適用されます。

それでは、JavaScriptコードに移りましょう。 外部JavaScriptファイルを作成することも、HTMLファイルに<script>タグを追加することもできます。 まず、すべての.box要素のDOMセレクターを作成します。

const boxes = document.querySelectorAll('.box');

次に、entriesというパラメーターを受け取るコールバック関数を使用して新しいResizeObserverをインスタンス化します。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {

});

entriesの各entryを反復処理するfor...ofループを作成します。 ループ内で、entry.target.querySelector('.info')と等しく設定される定数変数infoElを作成します。 これは、.info要素をtargetとして指します。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
  }
});

定数変数widthheightを作成し、それぞれentry.contentRect.widthentry.contentRect.heightに設定します。 Math.floorを両方に適用して、値を切り捨てます。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);
  }
});

画面の幅に応じて変化する角度でグラデーションを作成するため、width / 360 * 100と等しく設定されるangle変数を作成します。 ここでも、Math.floorを使用して、この値を切り捨てます。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
  }
});

線形グラデーションのコードを保持するgradientという定数を作成します。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
    const gradient = `linear-gradient(${ angle }deg, rgba(0,143,104,1), rgba(250,224,66,1))`;
  }
});

このグラデーションを設定したら、entry.target.style.backgroundを使用して、ターゲットエントリの背景をgradientに設定する必要があります。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
    const gradient = `linear-gradient(${ angle }deg, rgba(0,143,104,1), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;
  }
});

画面上のwidthheightの値を変更として確認すると便利です。 infoElinnerTextを取り、I'm ${width}px and ${height}px tallに設定します。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
    const gradient = `linear-gradient(${ angle }deg, rgba(0,143,104,1), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;

    infoEl.innerText = `I'm ${width}px and ${height}px tall`;
  }
});

ResizeObserverのコールバックが完了しました。 これで、forEachループを使用して、myObserver関数をboxesに適用できます。

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
    const gradient = `linear-gradient(${ angle }deg, rgba(0,143,104,1), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;

    infoEl.innerText = `I'm ${width}px and ${height}px tall`;
  }
});

boxes.forEach(box => {
  myObserver.observe(box);
});

観察できる要素を繰り返し処理し、各要素でobserveを呼び出す必要があることに注意してください。

これは、レスポンシブデザインにResizeObserverを使用する方法の実例です。 ただし、このようなJavaScript機能に対するブラウザのサポートに注意することが重要です。

ステップ3—ブラウザサポートの評価

Resize Observerのブラウザサポートは、現時点ではそれほど広範囲ではありません。 ありがたいことに、その間に使用できるポリフィルがあります。 ポリフィルは、 MutationObserverAPIに基づいています。

resizeobserverを使用できますか?にアクセスして、主要なブラウザー全体でこの機能のサポートを追跡できます。

結論

このチュートリアルでは、Reserve Observerで何ができるかを理解し、JavaScriptコードで使用して、ブラウザーのサポートをテストすることができました。

次のステップでは、ReactVueなどのJavaScriptフレームワークを使用してレスポンシブデザインに取り組む方法を学ぶことに興味があるかもしれません。