JavaScript 尺寸与滚动
尺寸和滚动是日常使用网页经常遇到的,包括浏览页面视窗大小和页面滚动,以及元素的尺寸和内部内容的滚动(设置了 overflow)也是十分常见的交互,相关属性值都可以通过 JavaScript 获取。
元素大小与滚动
JavaScript 中有许多节点属性可让我们读取有关元素的宽度、高度和其他几何特征的信息,这些属性值都是数值,它们是基于像素进行测量的。
<!-- 元素及尺寸设置 -->
<!-- 它有边框 border,内边距 padding 和滚动条,但没有外边距 margin,因为它们不是元素本身的一部分 -->
<div id="example">
...Text...
</div>
<style>
#example {
width: 300px;
height: 200px;
border: 25px solid #E8C48F;
padding: 20px;
overflow: auto;
}
</style>


offsetParent获取最接近的 CSS 定位的祖先元素节点(即设置了属性position为absolute/relative/fixed的祖先元素节点),或者是td,th,table,body节点html<main style="position: relative" id="main"> <article> <div id="example" style="position: absolute; left: 180px; top: 180px">...</div> </article> </main> <script> alert(example.offsetParent.id); // main alert(example.offsetLeft); // 180(注意:这是一个数字,不是字符串 "180px") alert(example.offsetTop); // 180 </script>offsetLeft/offsetTop获取元素相对于offsetParent祖先元素的左侧/上方边缘的距离offsetWidth/offsetHeight元素的宽/高(是指包括边框,内边距、内容的完整元素大小)clientLeft/clientTop内侧与外侧的距离,即从元素左上角外角到左上角内角的距离。对于从左到右显示内容的操作系统来说,它们始终是左侧/顶部 border 的宽度。Tip
而对于从右到左显示内容的操作系统来说,垂直滚动条在左边,所以
clientLeft也包括滚动条的宽度。clientWidth/clientHeight内容的宽/高,包括padding,但不包括滚动条 scrollbarscrollWidth/scrollHeight内容的宽/高,就像clientWidth/clientHeight一样,但还包括元素的滚动出的不可见的部分。Tip
通过修改
scrollLeft/scrollTop浏览器会滚动对应的元素。Tip
利用这个属性可以将元素展开 expand
js// 将元素展开(expand)到完整的内容高度 element.style.height = `${element.scrollHeight}px`;scrollLeft/scrollTop从元素的左上角开始,滚动出元素的上半部分的宽/高,即表示滚动了多少像素。Warning
如果一个元素(或其任何祖先)具有
display:none或不在文档中,则所有几何属性均为零(或offsetParent为null)。当我们创建了一个元素,但尚未将其插入文档中,或者它(或它的祖先)具有display:none时,则offsetParent为null,并且offsetWidth和offsetHeight为0,我们可以用这个特性来检查一个元素是否被隐藏jsfunction isHidden(elem) { return !elem.offsetWidth && !elem.offsetHeight; }
Tip
除了 scrollLeft/scrollTop 外,元素的所有几何属性都是只读的。
Tip
当元素设置了 overflow 并且文本溢出而含有滚动条时,元素的尺寸度量会变得十分复杂,一些浏览器(并非全部)通过从内容宽度 content width 中获取空间来为滚动条保留空间,如元素的内容宽度设置为 300 px,但滚动条宽度是 16px(不同的设备和浏览器,滚动条的宽度可能有所不同),那么实际容纳内容的宽度只有 300 - 16 = 284px

应该避免使用方法 getComputedStyle(elem) 来读取元素的 CSS 宽 width 和高 height,因为 CSS 属性 width/height 取决于另一个属性 box-sizing;而且 CSS 的 width/height 可能是 auto,如内联(inline)元素,获取的不是真正的元素尺寸;当元素含有滚动条时,有的浏览器(如 Chrome)返回的是实际内部宽度减去滚动条宽度,而某些浏览器(如 Firefox)返回的是 CSS 宽度(忽略了滚动条),这种跨浏览器的差异是不使用 getComputedStyle 而依靠几何属性的原因。
视窗大小和滚动
使用根文档元素节点 document.documentElement 的属性 clientWidth 或 clientHeight 获取窗口(window)的宽度和高度。

Tip
浏览器支持 window.innerWidth/innerHeight 属性,但它们返回的是包括滚动条的窗口大小(假设页面有滚动条),而 document.documentElement.clientWidth/clientHeight 是减去滚动条宽度后可用于内容的显示,文档的可见部分的视窗大小,它们一般比前者大小更小。在大多数情况下,我们需要 可用 的窗口宽度以绘制或放置某些东西,所以一般应该使用 documentElement.clientHeight/Width 获取窗口尺寸大小。
Warning
当 HTML 中没有 <!DOCTYPE HTML> 时,顶层级(top-level)几何属性的工作方式可能就会有所不同,可能会出现一些不可预计的错误情况。因此在现代 HTML 中始终都应该写 DOCTYPE。
对于页面的宽高可以使用根文档元素节点 document.documentElement 的属性 scrollWidth/ScrollHeight 获取文档的完整大小。
Warning
但在 Chrome/Safari/Opera 浏览器中,如果没有滚动条 document.documentElement.scrollHeight 甚至可能小于 document.documentElement.clientHeight 理论上讲,这些不一致来源于远古时代,而非「聪明」的逻辑。为了可靠地获得完整的文档高度,我们应该采用以下这些属性的最大值:
let scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
alert('Full document height, with scrolled out part: ' + scrollHeight);
获取当前滚动
文档的滚动状态可以使用 document.documentElement.scrollLeft/Top 获取,但在较旧的基于 WebKit 的浏览器中则不行,如在 Safari(bug 5991)中,我们应该使用 document.body 而不是 document.documentElement。💡 或者使用更通用的方法 window.pageXOffset/pageYOffset 获取。
滚动页面
有多种方法实现页面滚动:
- 通过赋值修改属性
document.documentElement.scrollTop/Left滚动页面(Safari 除外,而应该使用document.body.scrollTop/Left代替)。 - 通过方法
window.scrollBy(x,y)和window.scrollTo(pageX,pageY)滚动页面,它们更简单且适用于所有浏览器。- 方法
scrollBy(x,y)将页面滚动至 相对于当前位置的(x, y)位置。例如,scrollBy(0,10)会将页面向下滚动10px。 - 方法
scrollTo(pageX,pageY)将页面滚动至 绝对坐标,使得可见部分的左上角具有相对于文档左上角的坐标(pageX, pageY)。就像设置了scrollLeft/scrollTop一样。
- 方法
- 通过方法
elem.scrollIntoView(top)将滚动页面以使elem元素可在视窗中显示,其参数top是一个布尔值,用于设置元素elem滚动后的定位。- 如果
top=true(默认值),页面滚动使elem出现在窗口顶部,即元素的上边缘与窗口顶部对齐。 - 如果
top=false,页面滚动使elem出现在窗口底部,即元素的底部边缘与窗口底部对齐。
- 如果
Warning
必须在 DOM 完全构建好之后才能通过 JavaScript 滚动页面,如果尝试从 <head> 中的脚本滚动页面,它将无法正常工作。
禁止滚动
有时候我们需要使文档「不可滚动」,如网页一般会使用弹出框显示一条信息以立即引起注意时(消息覆盖在文档上,如 Bootstrap 库中的 modal 插件),我们希望访问者与该消息而不是与文档进行交互,因此需要禁止页面滚动,需要设置 document.body.style.overflow = "hidden" 这样页面就会「冻结」在当前位置,可以通过 document.body.style.overflow='' 取消禁止命令。
Tip
这个方法的缺点是会使滚动条消失,如果滚动条占用了一些空间,它原本占用的空间就会空出来,那么内容就会「跳」进去以填充它使得页面布局有细微的改变,在视觉上有卡顿感,这看起来有点奇怪。我们可以对比冻结前后的 clientWidth 如果它增加了(滚动条消失后),那么我们可以在 document.body 中滚动条原来的位置处通过添加 padding 来替代滚动条,保持了滚动条冻结前后文档内容宽度相同。