ResizeObserver - 元素resize監聽API


Motivation

響應式網站/Web應用程序 根據視口大小調整內容展示方式。這通常通過CSS和media查詢來完成。當CSS表現不好我們會使用Javascript。

比如document.addEventListener("resize",fun)或者Element的onresize屬性。通過監聽window.resize事件,Javascript DOM操作與視口大小保持同步。

但你會意識到,這甚至不包括在窗口未被調整大小但元素改變其大小的情況。例如,添加新的子元素,設置元素的display樣式none或類似的操作會改變元素,其兄弟或祖先的大小。

隨着響應式Web應用程序的普及,對響應式組件的需求也會增加。這些組件也需要有對resize事件做出響應。不幸的是,Web平台目前不提供組件跟蹤其大小的方法。

Current workarounds

一些應用程序實施自制的調整大小通知框架(例如:Polymer)。這種方法容易出錯,難以維護,並且需要每個組件都實施自制方法。

其他人巧妙的通過可以代替調整事件的事件來調整內容(例如:<a href="https://github.com/wnr/element-resize-detector">element-resize-detector</a>)。目前最優秀的方法都使用類似的技巧:

在組件中插入一個絕對定位的子項,並且以發出滾動事件的方式制作子項,或者在父項大小更改時制作window.resize。

絕對定位的子項方法在ShadowDOM或React等框架中不起作用。

這些方法都不可取。它們在正確性,代碼復雜性和性能方面都失敗了。

在當今的Web平台上無法復制ResizeObserver功能。

這就是為什么ResizeObserver是一個有用的原始API。它對任何觀察到的元素的大小的變化作出反應,與導致變化的原因無關。它還為您提供訪問觀察元素的新大小。

API

提到的“Observer”后綴的API共享一個簡單的API設計。ResizeObserver也不例外。

您創建一個ResizeObserver 對象並將回調傳遞給構造函數。回調將被賦予一個數組ResizeOberverEntries- 每個觀察元素一個條目 - 包含元素的大小

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);

Usage examples

以下是使用ResizeObserver在畫布內繪制橢圓的示例。

<canvas style="width:10%;height:10%"></canvas>
<canvas style="width:20%;height:20%"></canvas>
function drawEllipse(entry) {
let ctx = entry.target.getContext('2d');
let rx = Math.floor(entry.contentRect.width / 2);
let ry = Math.floor(entry.contentRect.height / 2);
ctx.beginPath();
ctx.clearRect(0,0, entry.contentRect.width,entry.contentRect.height);
ctx.ellipse(rx, ry, rx, ry, 0, 0, 2 * Math.PI);
ctx.stroke();
}
// ResizeObserver delegates action to Element's handleResize method
let ro = new ResizeObserver( entries => {
for (let entry of entries) {
if (entry.target.handleResize)
entry.target.handleResize(entry);
}
});
// Set up observations
var canvases = document.querySelectorAll('canvas');
for (let canvas of canvases) {
canvas.handleResize = drawEllipse;
ro.observe(canvas);
}

內聯框架可以檢測其大小何時發生變化,並通知父窗口。

let ro = new ResizeObserver(entries => {
let idealSize = computeIdealSize();
window.parent.postMessage({
name: "iframeResize",
width: idealSize.width,
height: idealSize.height
}, '*');
});
ro.observe(document.body);

當新消息到達時,我們如何讓聊天窗口滾動到底部?ResizeObserver解決方案將所有消息保存在不斷增長的中div,並觀察其大小。當新消息到達時,滾動到底部。
完整的例子 詳細討論了用戶滾動的處理。

.chat {
overflow: scroll;
}
<div class="chat"> <!-- chat has the scrollbar -->
<div class="chat-text"> <!-- chat-text contains chat text -->
<div>jack: hi </div>
<div>jill: hi </div>
</div>
</div
let ro = new ResizeObserver( entries => {
for (let e of entries) {
let chat = e.target.parentNode;
chat.scrollTop = chat.scrollHeight - chat.clientHeight;
}
});
ro.observe(document.querySelector('.chat-text'))

How

 

 

Performance

調整通知的大小可以有很高的頻率。Observer API避免了事件捕獲/泡泡的開銷。

框架作者可以在ResizeObserver之上提供一個開發友好的“基於事件”的API,以避免注冊太多的觀察者。

 

Notice

通知傳送順序

當多個ResizeObservers注冊時,通知應按注冊順序傳送。

回調變更集應按注冊順序列出元素。

內聯元素

內聯元素不應該生成調整大小通知。

怎么樣變換?

轉換不會影響內容大小。他們不應該觸發通知。

動畫怎么樣?

影響內容大小的動畫應該會觸發通知。

如果工作成本很高,開發人員可能會希望在動畫期間跳過工作。

調整大小和可視性

當元素不可見時,內容大小變為0。這將生成一個調整大小的通知。開發人員將能夠使用ResizeObserver觀察可見性。

 

本文參考:

ResizeObserver: It’s Like document.onresize for Elements ----- https://developers.google.com/web/updates/2016/10/resizeobserver

WICG/ResizeObserver ----- https://github.com/WICG/ResizeObserver/blob/master/explainer.md


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM