ResizeObserver

Baseline 已廣泛支援

此特性已經十分成熟,可在許多裝置和瀏覽器版本上使用。自 2020 年 7 月以來,它已在各大瀏覽器中可用。

ResizeObserver 介面報告 Element 內容框或邊框框尺寸的更改,或 SVGElement 的邊界框的更改。

注意:內容框是可以放置內容的框,即邊框框減去內邊距和邊框寬度。邊框框包含內容、內邊距和邊框。請參閱 盒模型 以獲得進一步解釋。

建構函式

ResizeObserver()

建立並返回一個新的 ResizeObserver 物件。

例項屬性

無。

例項方法

ResizeObserver.disconnect()

取消觀察特定觀察器所有被觀察的 Element 目標。

ResizeObserver.observe()

開始觀察指定的 Element

ResizeObserver.unobserve()

停止觀察指定的 Element

示例

resize-observer-text.html (檢視原始碼) 示例中,我們使用 resize observer 在滑塊值改變導致包含的 <div> 寬度改變時,更改標題和段落的 font-size。這表明您可以響應元素大小的變化,即使這些變化與視口無關。

我們還提供了一個複選框來開啟和關閉觀察器。如果關閉,文字將不會響應 <div> 寬度變化而改變。

JavaScript 程式碼如下

js
const h1Elem = document.querySelector("h1");
const pElem = document.querySelector("p");
const divElem = document.querySelector("body > div");
const slider = document.querySelector('input[type="range"]');
const checkbox = document.querySelector('input[type="checkbox"]');

divElem.style.width = "600px";

slider.addEventListener("input", () => {
  divElem.style.width = `${slider.value}px`;
});

const resizeObserver = new ResizeObserver((entries) => {
  for (const entry of entries) {
    if (entry.contentBoxSize) {
      const contentBoxSize = entry.contentBoxSize[0];
      h1Elem.style.fontSize = `${Math.max(
        1.5,
        contentBoxSize.inlineSize / 200,
      )}rem`;
      pElem.style.fontSize = `${Math.max(
        1,
        contentBoxSize.inlineSize / 600,
      )}rem`;
    } else {
      h1Elem.style.fontSize = `${Math.max(
        1.5,
        entry.contentRect.width / 200,
      )}rem`;
      pElem.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
    }
  }

  console.log("Size changed");
});

resizeObserver.observe(divElem);

checkbox.addEventListener("change", () => {
  if (checkbox.checked) {
    resizeObserver.observe(divElem);
  } else {
    resizeObserver.unobserve(divElem);
  }
});

觀察錯誤

遵循規範的實現會在繪製前(即在幀顯示給使用者之前)呼叫 resize 事件。如果有任何 resize 事件,將重新評估樣式和佈局 — 這反過來可能會觸發更多 resize 事件。由迴圈依賴引起的無限迴圈透過在每次迭代中僅處理 DOM 中更深的元素來解決。不滿足此條件的 resize 事件將推遲到下一個繪製,並在 Window 物件上觸發一個錯誤事件,帶有明確定義的字串訊息

ResizeObserver loop completed with undelivered notifications.

請注意,這僅能防止使用者代理鎖定,而不能防止無限迴圈本身。例如,以下程式碼將導致 divElem 的寬度無限增長,並在每個幀中在控制檯中重複出現上述錯誤訊息

js
const divElem = document.querySelector("body > div");

const resizeObserver = new ResizeObserver((entries) => {
  for (const entry of entries) {
    entry.target.style.width = `${entry.contentBoxSize[0].inlineSize + 10}px`;
  }
});

resizeObserver.observe(divElem);

window.addEventListener("error", (e) => {
  console.error(e.message);
});

只要錯誤事件不無限觸發,resize observer 就會穩定下來併產生一個穩定、可能正確的佈局。但是,訪問者可能會看到短暫的損壞佈局,因為本應在一個幀中發生的一系列更改卻發生在多個幀中。

如果您想防止這些錯誤,解決方案取決於您的預期效果。如果您確實打算實現無限迴圈,只需將 ResizeObserver 回撥中的重置程式碼推遲到瀏覽器重繪之後即可。您可以將其放入 requestAnimationFrame 回撥中。

js
const divElem = document.querySelector("body > div");

const resizeObserver = new ResizeObserver((entries) => {
  requestAnimationFrame(() => {
    for (const entry of entries) {
      entry.target.style.width = `${entry.contentBoxSize[0].inlineSize + 10}px`;
    }
  });
});

resizeObserver.observe(divElem);

window.addEventListener("error", (e) => {
  console.error(e.message);
});

如果您無意實現無限迴圈,則應確保您的重置程式碼不會觸發 resize observer 回撥。有多種方法可以實現這一點,例如設定一個“預期大小”,如果大小已經達到該值則不進行重置。

js
const divElem = document.querySelector("body > div");
const expectedSizes = new WeakMap();

const resizeObserver = new ResizeObserver((entries) => {
  requestAnimationFrame(() => {
    for (const entry of entries) {
      const expectedSize = expectedSizes.get(entry.target);
      if (entry.contentBoxSize[0].inlineSize === expectedSize) {
        continue;
      }
      const newSize = entry.contentBoxSize[0].inlineSize + 10;
      entry.target.style.width = `${newSize}px`;
      expectedSizes.set(entry.target, newSize);
    }
  });
});

resizeObserver.observe(divElem);

window.addEventListener("error", (e) => {
  console.error(e.message);
});

規範

規範
Resize Observer(調整大小觀察器)
# resize-observer-interface

瀏覽器相容性

另見