在前面的章节中,我们围绕 CSSOM 的核心做了两件事:读取样式(通过 getComputedStyle)和 修改样式(通过操作样式表或 element.style)。
但 CSSOM 的能力远不止于此。
回想一下你日常开发中的这些场景:
页面滚动到某个位置时,导航栏自动吸顶。
点击“回到顶部”按钮,页面平滑滚动上去。
实现一个图片懒加载 —— 当图片进入视口时才加载。
获取某个元素在页面中的精确位置,用于拖拽或定位。
这些功能都涉及一个共同的领域:视图(View) —— 窗口有多大?页面滚动了多少?元素在屏幕上的什么位置?
这些需求,属于 CSSOM 的另一个重要组成部分:CSSOM View。
CSSOM View 模块在 Window、Document、Element 等接口上扩展了大量属性和方法。它不直接操作样式本身,而是操作样式的 呈现结果——窗口的位置与大小、页面的滚动状态、元素的布局信息。本章将系统性地梳理这些 API,让你在面对“与视图相关”的需求时,知道该用什么工具。
一、窗口 API —— 控制浏览器窗口本身
窗口 API 提供了一组方法,用于控制浏览器窗口的位置和大小。这些方法直接作用于 window 对象。
1.1 移动窗口:moveTo() 与 moveBy()
moveTo(x, y) 将窗口移动到屏幕上的绝对位置:
// 将窗口移动到屏幕左上角
window.moveTo(0, 0);
// 将窗口移动到屏幕中央(假设屏幕分辨率为 1920×1080)
window.moveTo(460, 240);moveBy(x, y) 将窗口相对于当前位置移动指定的偏移量:
// 将窗口向右移动 100 像素,向下移动 50 像素
window.moveBy(100, 50);
// 向左移动(负值)
window.moveBy(-50, 0);1.2 调整窗口大小:resizeTo() 与 resizeBy()
resizeTo(width, height) 将窗口调整为指定的宽度和高度:
// 将窗口调整为 800×600
window.resizeTo(800, 600);resizeBy(x, y) 将窗口的宽度和高度分别增加指定的像素值:
// 窗口宽度增加 100px,高度增加 50px
window.resizeBy(100, 50);
// 缩小窗口
window.resizeBy(-50, -30);1.3 通过 window.open() 控制新窗口
window.open() 的 第三个参数 是一个逗号分隔的特性字符串,用于控制新窗口的外观和行为。
// 打开一个 400×300 的新窗口,位于屏幕 (100, 100) 位置
const newWindow = window.open(
'https://example.com',
'exampleWindow',
'width=400,height=300,left=100,top=100'
);常用的窗口特性包括:
特性 | 描述 |
|---|---|
width=number | 窗口宽度(像素) |
height=number | 窗口高度(像素) |
left=number | 窗口距屏幕左侧的距离 |
top=number | 窗口距屏幕顶部的距离 |
resizable=yes/no | 窗口是否可调整大小 |
重要提醒
这些窗口控制方法在现代浏览器中受到严格限制:
必须由用户操作触发:moveTo、moveBy、resizeTo、resizeBy 以及 window.open 都必须在用户手势(如点击事件)中调用,否则会被浏览器拦截。
不能跨域控制:只能控制当前浏览器标签页的窗口,不能控制其他标签页或跨域窗口。
弹窗拦截:window.open 如果不在用户手势中调用,会被浏览器视为弹窗而拦截。
因此,在实际开发中,这些 API 的使用场景比较有限——通常用于一些特殊的 Web 应用(如在线编辑器、控制台工具)或企业内部系统。
开发建议:除非你有明确的、必须控制浏览器窗口的需求,否则在日常 Web 开发中,这些 API 用得不多。相比之下,本章后续的滚动 API 和布局 API 才是你更常用的工具。
二、滚动 API —— 操控页面的滚动
滚动是 Web 开发中最常见的交互之一。CSSOM View 提供了从“全局页面滚动”到“单个元素滚动”的完整控制能力。
2.1 获取视口滚动位置
要获取页面当前滚动了多少,可以使用 window.scrollX 和 window.scrollY(或它们的别名 window.pageXOffset 和 window.pageYOffset)。
// 获取水平滚动距离
console.log(window.scrollX); // 推荐
console.log(window.pageXOffset); // 别名,兼容性更好
// 获取垂直滚动距离
console.log(window.scrollY); // 推荐
console.log(window.pageYOffset); // 别名,兼容性更好小贴士:pageXOffset / pageYOffset 是更早的规范,兼容性更好。不过在现代浏览器中,scrollX / scrollY 已经得到广泛支持,两者完全等价。
2.2 控制页面滚动:scroll()、scrollTo()、scrollBy()
这三个方法都用于控制页面滚动,区别在于 滚动目标 的指定方式不同。
scrollTo(x, y) —— 滚动到指定的绝对位置:
// 滚动到页面顶部
window.scrollTo(0, 0);
// 滚动到水平 100px、垂直 500px 的位置
window.scrollTo(100, 500);scrollBy(x, y) —— 从当前位置滚动指定的偏移量:
// 向下滚动 200 像素
window.scrollBy(0, 200);
// 向右滚动 100 像素,向下滚动 50 像素
window.scrollBy(100, 50);scroll() —— 与 scrollTo() 完全等价,只是名称不同。
平滑滚动
以上三个方法都支持传入一个 配置对象 来实现平滑滚动:
// 使用配置对象实现平滑滚动到顶部
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth' // 'smooth' | 'instant' | 'auto'
});
// 平滑向下滚动 300 像素
window.scrollBy({
top: 300,
left: 0,
behavior: 'smooth'
});behavior 的可选值:
'smooth':平滑动画滚动。
'instant':瞬间跳转。
'auto':由 CSS 的 scroll-behavior 属性决定。
体验提示:平滑滚动能显著提升用户体验,尤其是在“回到顶部”这类操作中。推荐在适当的场景中使用 behavior: 'smooth'。
2.3 元素滚动属性:scrollTop、scrollLeft、scrollWidth、scrollHeight
除了控制整个页面的滚动,我们还可以控制 单个可滚动元素 的滚动状态。
滚动位置属性(可读写) :
element.scrollTop:元素内容垂直向上滚动的像素值(即顶部被隐藏的高度)。
element.scrollLeft:元素内容水平向左滚动的像素值(即左侧被隐藏的宽度)。
const container = document.querySelector('.scrollable-container');
// 读取当前滚动位置
console.log('垂直滚动:', container.scrollTop);
console.log('水平滚动:', container.scrollLeft);
// 设置滚动位置 —— 滚动到顶部
container.scrollTop = 0;
// 滚动到底部
container.scrollTop = container.scrollHeight - container.clientHeight;内容尺寸属性(只读) :
element.scrollWidth:元素内容的总宽度(包括溢出部分),包含内边距但不包含边框和外边距。
element.scrollHeight:元素内容的总高度(包括溢出部分),包含内边距但不包含边框和外边距。
// 判断是否可滚动(内容是否溢出)
const canScroll = container.scrollHeight > container.clientHeight;
// 计算滚动进度(0~1)
const progress = container.scrollTop / (container.scrollHeight - container.clientHeight);
console.log('滚动进度:', (progress * 100).toFixed(0) + '%');2.4 scrollIntoView() —— 将元素滚动到可见区域
scrollIntoView() 是 Element 接口上的方法,它会滚动元素的祖先容器,使得调用该方法的元素对用户可见。
这是前端开发中最常用的滚动 API 之一,比如:表单项校验失败时自动滚动到错误位置、点击导航菜单滚动到对应章节等。
基本用法:
const element = document.getElementById('target');
// 默认行为:元素顶部对齐到视口顶部
element.scrollIntoView();
// 等价于
element.scrollIntoView(true); // true = 顶部对齐
element.scrollIntoView(false); // false = 底部对齐使用配置对象(推荐) :
element.scrollIntoView({
behavior: 'smooth', // 'smooth' | 'instant' | 'auto'
block: 'center', // 'start' | 'center' | 'end' | 'nearest'
inline: 'nearest' // 'start' | 'center' | 'end' | 'nearest'
});配置项说明:
选项 | 可选值 | 描述 |
|---|---|---|
behavior | 'smooth'、'instant'、'auto' | 滚动动画方式 |
block | 'start'、'center'、'end'、'nearest' | 垂直方向的对齐方式 |
inline | 'start'、'center'、'end'、'nearest' | 水平方向的对齐方式 |
// 将元素居中显示在视口中(垂直和水平都居中)
element.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});2.5 监听滚动事件
当用户滚动页面或可滚动元素时,会触发 scroll 事件。
// 监听整个页面的滚动
window.addEventListener('scroll', function() {
console.log('页面滚动位置:', window.scrollY);
});
// 监听某个元素的滚动
const container = document.querySelector('.scrollable');
container.addEventListener('scroll', function() {
console.log('元素滚动位置:', this.scrollTop);
});性能优化提示:scroll 事件触发的频率非常高(每帧都可能触发),在事件处理函数中 不要做复杂的计算或 DOM 操作。如果需要做复杂操作,可以使用 requestAnimationFrame 或 防抖(debounce) 来优化。
// 使用 requestAnimationFrame 优化滚动事件
let ticking = false;
window.addEventListener('scroll', function() {
if (!ticking) {
window.requestAnimationFrame(function() {
// 在这里做滚动相关的处理
console.log('滚动位置:', window.scrollY);
ticking = false;
});
ticking = true;
}
});补充:如果需要检测滚动是否结束,可以使用 scrollend 事件,它会在滚动完成时触发。
三、布局 API —— 获取元素的位置与尺寸
在开发中,我们经常需要知道一个元素在页面中的精确位置和尺寸——比如实现拖拽、定位工具提示、判断元素是否在视口内等。CSSOM View 提供了多种方式来获取这些信息。
视口尺寸(浏览器窗口中可见区域的尺寸):
// 视口尺寸(包含滚动条)
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// 视口尺寸(不包含滚动条)—— 通过 document.documentElement 获取
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;浏览器窗口外部尺寸(包含工具栏、地址栏等):
// 浏览器窗口的完整宽度和高度
const outerWidth = window.outerWidth;
const outerHeight = window.outerHeight;属性 | 包含内容 | 典型用途 |
|---|---|---|
window.innerWidth/Height | 视口(含滚动条) | 响应式布局、视口检测 |
document.documentElement.clientWidth/Height | 视口(不含滚动条) | 精确的可用宽度 |
window.outerWidth/Height | 整个浏览器窗口 | 较少使用 |
3.2 获取元素的位置与尺寸
getBoundingClientRect() 是最常用的元素位置获取方法。它返回一个 DOMRect 对象,包含了元素的大小及其 相对于视口 的位置信息。
const rect = element.getBoundingClientRect();
console.log('位置(相对于视口):');
console.log(' top:', rect.top); // 元素顶部距视口顶部的距离
console.log(' left:', rect.left); // 元素左侧距视口左侧的距离
console.log(' bottom:', rect.bottom); // 元素底部距视口顶部的距离
console.log(' right:', rect.right); // 元素右侧距视口左侧的距离
console.log('尺寸:');
console.log(' width:', rect.width); // 元素宽度(含 padding 和 border)
console.log(' height:', rect.height); // 元素高度(含 padding 和 border)关键理解:
1. getBoundingClientRect()返回的 top、left 是 相对于视口 的,会随着页面滚动而变化。
2. 如果需要获取元素 相对于整个文档 的位置,需要加上滚动偏移量:
// 获取元素相对于文档顶部的位置
const rect = element.getBoundingClientRect();
const absoluteTop = rect.top + window.scrollY;
const absoluteLeft = rect.left + window.scrollX;3. width 和 height 包含了 padding 和 border。
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top < window.innerHeight &&
rect.bottom > 0 &&
rect.left < window.innerWidth &&
rect.right > 0
);
}// 使用示例:图片懒加载
const images = document.querySelectorAll('.lazy-image');
images.forEach(img => {
if (isInViewport(img)) {
img.src = img.dataset.src;
img.classList.remove('lazy-image');
}
});3.3 offset 系列属性
除了 getBoundingClientRect(),还有一组 offset 属性 可以获取元素的位置信息。
offsetTop:元素上边框到其 offsetParent 上边框的垂直距离。
offsetLeft:元素左边框到其 offsetParent 左边框的水平距离。
offsetParent:距离该元素最近的 已定位(position 不为 static)的祖先元素。
// 获取元素相对于其 offsetParent 的位置
const top = element.offsetTop;
const left = element.offsetLeft;
const parent = element.offsetParent;
console.log('相对于 offsetParent:', top, left);
console.log('offsetParent:', parent);getBoundingClientRect() vs offset 属性:
维度 | getBoundingClientRect() | offsetTop / offsetLeft |
|---|---|---|
参考系 | 视口(会随滚动变化) | offsetParent (不随滚动变化) |
包含的内容 | padding + border + 内容 | 从边框到边框 |
是否只读 | 是 | 是 |
使用场景 | 视口相关判断(懒加载、吸顶) | 相对于父元素的定位计算 |
3.4 client 系列属性
还有一组 client 属性,主要用于获取元素的内部尺寸:
element.clientWidth:元素内部宽度(内容区 + padding,不含 border 和滚动条)。
element.clientHeight:元素内部高度(内容区 + padding,不含 border 和滚动条)。
element.clientTop:元素上边框的宽度。
element.clientLeft:元素左边框的宽度。
// 获取元素的可视内容区域尺寸(不含滚动条)
const visibleWidth = element.clientWidth;
const visibleHeight = element.clientHeight;
// 获取边框宽度
const borderTop = element.clientTop;
const borderLeft = element.clientLeft;四、综合示例:实现一个“回到顶部”按钮
将本章的多个知识点整合到一个完整的示例中:
<style>
body {
height: 2000px;
font-family: Arial, sans-serif;
}
.back-to-top {
position: fixed;
bottom: 40px;
right: 40px;
padding: 12px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
pointer-events: none;
}
.back-to-top.visible {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.back-to-top:hover {
background: #0056b3;
}
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(to right, #007bff, #00d4ff);
width: 0%;
transition: width 0.1s;
z-index: 999;
}
</style><div class="progress-bar" id="progressBar"></div>
<button class="back-to-top" id="backToTop">↑ 回到顶部</button>
<h1>滚动示例页面</h1>
<p style="margin-top: 800px;">向下滚动看看效果</p><script>
const backBtn = document.getElementById('backToTop');
const progressBar = document.getElementById('progressBar');
// 监听页面滚动
window.addEventListener('scroll', function() {
const scrollY = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
// 1. 更新进度条
const progress = (scrollY / docHeight) * 100;
progressBar.style.width = progress + '%';
// 2. 控制“回到顶部”按钮的显示/隐藏
if (scrollY > 300) {
backBtn.classList.add('visible');
} else {
backBtn.classList.remove('visible');
}
});
// 点击按钮平滑滚动到顶部
backBtn.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
// 示例:获取某个元素的位置
const title = document.querySelector('h1');
const rect = title.getBoundingClientRect();
console.log('标题相对于视口的位置:', rect.top, rect.left);
console.log('标题相对于文档的位置:', rect.top + window.scrollY, rect.left + window.scrollX);
</script>这个示例展示了:
使用 window.scrollY 监听滚动位置。
使用 scrollTo() 实现平滑滚动回到顶部。
使用 getBoundingClientRect() 获取元素位置。
结合 CSS 类切换实现 UI 状态管理。
五、小结
本章我们完整学习了 CSSOM View 模块的核心功能:
1. 窗口 API
moveTo() / moveBy() —— 控制窗口位置。
resizeTo() / resizeBy() —— 控制窗口大小。
window.open() 的第三个参数控制新窗口尺寸。
这些 API 受到严格的用户手势限制,使用场景有限。
2. 滚动 API
scrollX / scrollY(或 pageXOffset / pageYOffset)—— 获取视口滚动位置。
scrollTo() —— 滚动到指定位置。
scrollBy() —— 相对当前位置滚动。
通过 { behavior: 'smooth' } 实现平滑滚动。
元素级别的 scrollTop / scrollLeft(可读写)和 scrollWidth / scrollHeight(只读)。
scrollIntoView() —— 将元素滚动到可见区域,支持精细的对齐控制。
scroll 事件监听滚动,注意性能优化。
3. 布局 API
window.innerWidth / innerHeight —— 视口尺寸(含滚动条)。
document.documentElement.clientWidth / clientHeight —— 视口尺寸(不含滚动条)。
getBoundingClientRect() —— 获取元素相对于视口的位置和尺寸(最常用)。
offsetTop / offsetLeft / offsetParent —— 获取元素相对于定位父元素的位置。
clientWidth / clientHeight —— 获取元素内部可视尺寸。
CSSOM View 是你日常开发中使用频率最高的 API 集合之一。从“回到顶部”到“图片懒加载”,从“滚动吸顶”到“元素定位”,这些 API 几乎出现在每一个交互稍复杂的页面中。掌握它们,你就能自信地应对绝大多数与“视图”相关的需求。
在下一章中,我们将进入 CSSOM 系列的一个进阶话题——CSS 类型对象模型(CSS Typed OM),了解如何用类型化的方式更高效、更安全地操作 CSS 值。