ResizeObserver 之前,您必須將監聽器附加至文件的 resize 事件,才能在視埠尺寸有任何變更時收到通知。在事件處理常式中,您必須找出受該變更影響的元素,並呼叫特定常式來適當反應。如果您需要調整大小後元素的全新尺寸,就必須呼叫 getBoundingClientRect() getComputedStyle() ,但如果您未妥善批次處理 所有 讀取和 所有 寫入作業,可能會導致版面配置顛簸。

這甚至未涵蓋元素在主視窗未調整大小的情況下變更大小的情況。舉例來說,附加新子項、將元素的 display 樣式設為 none ,或類似動作可能會變更元素、同層級元素或祖先元素的大小。

這就是 ResizeObserver 實用的原因。無論變更原因為何,只要觀察的任何元素大小發生變化,這個 Hook 就會做出反應。您也可以存取所觀察元素的新大小。

上述所有帶有 Observer 後置字元的 API 都採用簡單的 API 設計。 ResizeObserver 也不例外。您會建立 ResizeObserver 物件,並將回呼傳遞至建構函式。回呼會傳遞 ResizeObserverEntry 物件的陣列 (每個觀察到的元素各有一個項目),其中包含元素的新尺寸。

var ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;
    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
// Observe one or multiple elements
ro.observe(someElement);

系統會回報哪些資料?

一般來說,ResizeObserverEntry 會透過名為 contentRect 的屬性回報元素的內容方塊,並傳回 DOMRectReadOnly 物件。內容方塊是可放置內容的方塊。這是指邊框方塊減去邊框間距。

請注意,雖然 ResizeObserver reports contentRect 和邊框間距的維度,但只會 watches contentRect請勿contentRect 與元素的定界框混淆。getBoundingClientRect() 回報的邊界方塊是包含整個元素及其子系的方塊。SVG 是這項規則的例外狀況,其中 ResizeObserver 會回報定界框的尺寸。

自 Chrome 84 起,ResizeObserverEntry 新增了三項屬性,可提供更詳細的資訊。這些屬性都會傳回 ResizeObserverSize 物件,其中包含 blockSize 屬性和 inlineSize 屬性。這項資訊與回呼叫用時觀察到的元素有關。

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize
  • 所有這些項目都會傳回唯讀陣列,因為我們希望日後這些項目能支援具有多個片段的元素,這類元素會出現在多欄情境中。目前這些陣列只會包含一個元素。

    這些屬性的平台支援有限,但 Firefox 已支援前兩項。

    何時回報?

    規格規定 ResizeObserver 應在繪製前和版面配置後處理所有大小調整事件。因此,ResizeObserver 的回呼是變更網頁版面配置的理想位置。由於 ResizeObserver 處理作業會在版面配置和繪製之間進行,因此這麼做只會使版面配置失效,不會使繪製失效。

    Gotcha

    您可能會想問:如果我在回呼中將受監控元素的大小變更為 ResizeObserver,會發生什麼事?答案是:您會立即觸發對回呼的另一次呼叫。幸好 ResizeObserver 具有避免無限回呼迴圈和循環依附元件的機制。只有在重新調整大小的元素位於 DOM 樹狀結構中,比前一個回呼中處理的最淺元素更深時,系統才會在同一個影格中處理變更。否則,系統會將其延後至下一個影格。

    ResizeObserver 的其中一項功能是實作每個元素的媒體查詢。觀察元素後,您就能強制定義設計中斷點,並變更元素的樣式。在下列範例中,第二個方塊會根據寬度變更邊框半徑。

    const ro = new ResizeObserver(entries => {
      for (let entry of entries) {
        entry.target.style.borderRadius =
            Math.max(0, 250 - entry.contentRect.width) + 'px';
    // Only observe the second box
    ro.observe(document.querySelector('.box:nth-child(2)'));
    

    另一個值得參考的有趣範例是聊天視窗。在一般的由上而下對話版面配置中,會出現捲動位置的問題。為避免使用者混淆,建議將視窗固定在對話底部,也就是顯示最新訊息的位置。此外,任何版面配置變更 (例如手機從橫向切換為直向,反之亦然) 也應達到相同效果。

    ResizeObserver 可讓您編寫單一程式碼,同時處理這兩種情況。根據定義,ResizeObserver 可以擷取視窗大小調整事件,但呼叫 appendChild() 也會調整該元素的大小 (除非設定 overflow: hidden),因為需要為新元素騰出空間。因此,您只需要幾行程式碼,就能達到所需效果:

    const ro = new ResizeObserver(entries => {
      document.scrollingElement.scrollTop =
        document.scrollingElement.scrollHeight;
    // Observe the scrollingElement for when the window gets resized
    ro.observe(document.scrollingElement);
    // Observe the timeline to process new messages
    ro.observe(timeline);
    

    從這裡開始,我可以新增更多程式碼來處理使用者手動向上捲動,並希望在收到新訊息時,捲動位置能停留在訊息的情況。

    另一種用途是處理自行排版的任何自訂元素。直到 ResizeObserver,如果尺寸變更,就沒有可靠的方式可取得通知,以便重新配置子項。

    對 Interaction to Next Paint (INP) 的影響

    Interaction to Next Paint (INP) 指標可評估網頁回應使用者互動的整體效能。如果網頁的 INP 處於「良好」門檻 (也就是 200 毫秒或更短),表示網頁能可靠地回應使用者與網頁的互動。

    雖然事件回呼回應使用者互動所需的時間,可能會大幅影響互動的總延遲時間,但這並非影響 INP 的唯一因素。INP 也會考量互動的下一次繪製所需時間。這是指完成算繪作業所需的時間,以便在使用者互動後更新使用者介面。

    ResizeObserver 而言,這點非常重要,因為 ResizerObserver 執行個體執行的回呼正好發生在算繪作業之前。這是設計使然,因為回呼中發生的工作必須納入考量,因為該工作的結果很可能需要變更使用者介面。

    請注意,在 ResizeObserver 回呼中,盡量只執行必要算繪工作,因為過多的算繪工作可能會導致瀏覽器延遲執行重要工作。舉例來說,如果任何互動的回呼會導致 ResizeObserver 回呼執行,請務必執行下列操作,盡可能提供流暢的體驗:

  • 請盡可能簡化 CSS 選擇器,避免過度重新計算樣式。樣式重新計算會在版面配置前進行,複雜的 CSS 選取器可能會延遲版面配置作業。
  • 請避免在 ResizeObserver 回呼中執行任何可能觸發強制重排的工作。
  • 一般來說,網頁上的 DOM 元素越多,更新網頁版面配置所需的時間就越長。無論網頁是否使用 ResizeObserver,這都是事實,但隨著網頁結構複雜度增加,ResizeObserver 回呼中完成的工作可能會變得相當重要。
  • ResizeObserver 適用於所有主要瀏覽器,可有效監控元素層級的元素大小調整作業。但請注意,別使用這個強大的 API 過度延遲算繪作業。

    除非另有註明,否則本頁面中的內容是採用創用 CC 姓名標示 4.0 授權,程式碼範例則為阿帕契 2.0 授權。詳情請參閱《Google Developers 網站政策》。Java 是 Oracle 和/或其關聯企業的註冊商標。

    上次更新時間:2023-12-12 (世界標準時間)。

    [[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2023-12-12 (世界標準時間)。"],[],[]]