pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:HTML5 Resize Observer - 完整教程与代码示例
一、教程简介
Resize Observer API 提供了监听元素尺寸变化的能力,当被观察元素的边界框大小发生改变时,会触发回调函数。相比传统的 window.resize 事件,Resize Observer 可以监听任意元素(不仅仅是窗口)的尺寸变化,且不会因频繁触发而导致性能问题。该 API 广泛应用于响应式组件、自适应布局、图表重绘、虚拟滚动等场景。
二、核心概念
与 window.resize 对比
ResizeObserverEntry
contentRect 属性
代码示例
entry.contentRect // DOMRectReadOnly
{
x: 0, // 相对于元素的 x 偏移
y: 0, // 相对于元素的 y 偏移
width: 300, // 内容宽度(不含 padding/border)
height: 200, // 内容高度(不含 padding/border)
top: 0,
right: 300,
bottom: 200,
left: 0
}三、语法与用法
创建与观察
代码示例
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
console.log('元素:', entry.target);
console.log('宽度:', entry.contentRect.width);
console.log('高度:', entry.contentRect.height);
}
});
observer.observe(element); // 开始观察
observer.unobserve(element); // 停止观察
observer.disconnect(); // 停止所有观察监听多个元素
代码示例
document.querySelectorAll('.resizable').forEach(el => {
observer.observe(el);
});使用 borderBoxSize
代码示例
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const size = entry.borderBoxSize[0];
console.log('边框盒宽度:', size.inlineSize);
console.log('边框盒高度:', size.blockSize);
}
});四、代码示例
示例一:响应式尺寸监控
以下是一个完整的响应式尺寸监控示例,展示了如何使用 Resize Observer 监听元素尺寸变化并实时更新界面:
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Resize Observer - 尺寸监控</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 30px 20px;
}
.container { max-width: 800px; margin: 0 auto; }
h1 { font-size: 24px; color: #2d3436; margin-bottom: 8px; }
.desc { color: #636e72; font-size: 14px; margin-bottom: 24px; }
.resizable-box {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 12px;
padding: 24px;
color: #fff;
resize: both;
overflow: hidden;
min-width: 150px;
min-height: 100px;
max-width: 100%;
margin-bottom: 16px;
position: relative;
}
.size-badge {
position: absolute;
bottom: 8px;
right: 8px;
background: rgba(0,0,0,0.4);
padding: 4px 10px;
border-radius: 6px;
font-size: 12px;
font-family: monospace;
}
.card {
background: #fff;
border-radius: 12px;
padding: 24px;
margin-bottom: 16px;
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
}
.card h2 { font-size: 16px; color: #2d3436; margin-bottom: 16px; }
.size-info {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.size-item {
background: #f8f9fa;
border-radius: 8px;
padding: 12px;
text-align: center;
}
.size-label { font-size: 11px; color: #999; margin-bottom: 4px; }
.size-value { font-size: 18px; font-weight: 700; color: #0984e3; }
.log-area {
background: #1e1e1e;
border-radius: 8px;
padding: 12px;
max-height: 200px;
overflow-y: auto;
font-family: monospace;
font-size: 12px;
line-height: 1.7;
color: #a0e0a0;
}
.responsive-grid {
display: grid;
gap: 12px;
transition: all 0.3s;
}
.responsive-grid.cols-1 { grid-template-columns: 1fr; }
.responsive-grid.cols-2 { grid-template-columns: 1fr 1fr; }
.responsive-grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
.grid-item {
background: #e8f0fe;
border-radius: 8px;
padding: 16px;
text-align: center;
font-size: 13px;
color: #1a73e8;
}
</style>
</head>
<body>
<div class="container">
<h1>Resize Observer 尺寸监控</h1>
<p class="desc">拖动右下角调整盒子大小,观察尺寸变化。</p>
<div class="resizable-box" id="resizableBox">
<div style="font-size:18px;font-weight:700;margin-bottom:8px;">可调整大小的盒子</div>
<div style="font-size:13px;opacity:0.8;">拖动右下角调整尺寸</div>
<div class="size-badge" id="sizeBadge">0 x 0</div>
</div>
<div class="card">
<h2>尺寸详情</h2>
<div class="size-info">
<div class="size-item">
<div class="size-label">宽度</div>
<div class="size-value" id="widthVal">0</div>
</div>
<div class="size-item">
<div class="size-label">高度</div>
<div class="size-value" id="heightVal">0</div>
</div>
<div class="size-item">
<div class="size-label">变化次数</div>
<div class="size-value" id="changeCount">0</div>
</div>
<div class="size-item">
<div class="size-label">列数</div>
<div class="size-value" id="colCount">1</div>
</div>
</div>
</div>
<div class="card">
<h2>响应式网格(自动调整列数)</h2>
<div class="responsive-grid cols-1" id="responsiveGrid">
<div class="grid-item">项目 1</div>
<div class="grid-item">项目 2</div>
<div class="grid-item">项目 3</div>
<div class="grid-item">项目 4</div>
<div class="grid-item">项目 5</div>
<div class="grid-item">项目 6</div>
</div>
</div>
<div class="card">
<h2>变化日志</h2>
<div class="log-area" id="logArea"></div>
</div>
</div>
<script>
let changeCount = 0;
const box = document.getElementById('resizableBox');
const grid = document.getElementById('responsiveGrid');
const observer = new ResizeObserver(function (entries) {
for (const entry of entries) {
const width = Math.round(entry.contentRect.width);
const height = Math.round(entry.contentRect.height);
if (entry.target === box) {
changeCount++;
document.getElementById('widthVal').textContent = width + 'px';
document.getElementById('heightVal').textContent = height + 'px';
document.getElementById('changeCount').textContent = changeCount;
document.getElementById('sizeBadge').textContent = width + ' x ' + height;
addLog('盒子尺寸变化: ' + width + ' x ' + height);
}
if (entry.target === grid) {
let cols = 1;
if (width >= 450) cols = 3;
else if (width >= 280) cols = 2;
grid.className = 'responsive-grid cols-' + cols;
document.getElementById('colCount').textContent = cols;
addLog('网格列数变化: ' + cols + ' 列 (宽度: ' + width + 'px)');
}
}
});
observer.observe(box);
observer.observe(grid);
function addLog(message) {
const log = document.getElementById('logArea');
const time = new Date().toLocaleTimeString('zh-CN');
const line = document.createElement('div');
line.textContent = '[' + time + '] ' + message;
log.insertBefore(line, log.firstChild);
while (log.children.length > 50) {
log.removeChild(log.lastChild);
}
}
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 避免无限循环
代码示例
// 在回调中修改被观察元素的尺寸可能导致循环
const observer = new ResizeObserver(entries => {
// 不要直接修改被观察元素的尺寸
// 如果必须修改,使用 requestAnimationFrame 延迟
requestAnimationFrame(() => {
// 修改尺寸
});
});2. 性能优化
代码示例
// 使用防抖减少回调频率
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const observer = new ResizeObserver(debounce(entries => {
// 处理尺寸变化
}, 100));3. 及时断开
代码示例
// 组件卸载时断开
observer.disconnect();七、代码规范示例
代码示例
class ElementResizeWatcher {
constructor(options = {}) {
this.debounceMs = options.debounceMs || 0;
this.callbacks = new Map();
this.observer = new ResizeObserver(this._handleResize.bind(this));
}
_handleResize(entries) {
entries.forEach(entry => {
const callback = this.callbacks.get(entry.target);
if (callback) {
callback({
width: entry.contentRect.width,
height: entry.contentRect.height,
target: entry.target
});
}
});
}
watch(element, callback) {
this.callbacks.set(element, callback);
this.observer.observe(element);
return this;
}
unwatch(element) {
this.callbacks.delete(element);
this.observer.unobserve(element);
return this;
}
destroy() {
this.observer.disconnect();
this.callbacks.clear();
}
}
// 使用
const watcher = new ElementResizeWatcher();
watcher.watch(document.getElementById('container'), ({ width, height }) => {
console.log('尺寸:', width, height);
});八、常见问题与解决方案
常见问题
Q1:初始尺寸如何获取?
Resize Observer 在首次 observe 时会立即触发一次回调,可以获取初始尺寸。
Q2:如何获取包含 padding/border 的尺寸?
使用 entry.borderBoxSize[0].inlineSize/blockSize 或 entry.target.offsetWidth/offsetHeight。
Q3:隐藏元素会触发回调吗?
元素从可见变为隐藏(display:none)时会触发一次回调(尺寸变为0),之后不再触发。
九、总结
Resize Observer 提供了监听任意元素尺寸变化的能力,比 window.resize 更灵活、更高效。通过 contentRect 和 borderBoxSize 可以获取精确的尺寸信息,适用于响应式布局、图表重绘、自适应组件等场景。使用时需注意避免在回调中修改被观察元素的尺寸(防止循环),及时断开不再需要的观察,以及合理使用防抖优化性能。
标签:
ResizeObserver
元素尺寸监听
响应式布局
自适应组件
性能优化
本文涉及AI创作
内容由AI创作,请仔细甄别