原文链接:https://javascript.info/size-and-scroll-window,translate with ❤️ by zhangbao.

怎样查看浏览器窗口的宽度?怎样获得整个页面的高度?怎样用 JavaScript 滚动页面?

从 DOM 的视角来看,文档根元素是 document.documentElement,对应 <html>,具有前一章中我们说过的几何属性。在一些场景里,除此之外,它还有其他一些重要的方法和特性可以使用。

窗口的宽/高

窗口宽高,使用 document.documentElement 对象上的 clientWidthclientHeight 属性获得:

窗口尺寸和滚动 - 图1

窗口尺寸和滚动 - 图2不要使用 **window.innerWidth/Height**``

虽然浏览器提供了 window.innerWidth/Height 接口,好像是我们想要的,那么它与 document.documentElement 对象上的 clientWidth/clientHeight 区别在哪里?

如果窗口存在滚动条的话,是会占据内容区空间的。我们已经知道,clientWidth/clientHeight 的结果是不包含滚动条的,也就是说,我们得到的是可视区内、可用内容的宽/高。

但是,widow.innerWIdth/innerHeight 属性值是包含滚动条的。

如果有滚动条的话,下面两行就会打印不同的值。

  1. alert( window.innerWidth ); // 窗口完整宽度
  2. alert( document.documentElement.clientWidth ); // 减去滚动条后的窗口宽度

很多时候,我们需要的是窗口的可用宽度,在其中(不包含滚动条)渲染和定位元素。所以,我们应该用 documentElement.clientHeight/Width 更为精确。

窗口尺寸和滚动 - 图3DOCTYPE`` 很重要

在我们的 HTML 文档里,如果我们没有使用文档声明 <!DOCTYPE html> 的话,顶级 的几个几何属性值可能会有差异(嗯,奇怪的事情总是有可能发生)。

在现代 HTML 我们总是会写 DOCTYPE,通常它对 JavaScript 代码不会产生影响,但在此处却是有影响的。

文档的宽/高

理论上来说,根元素是 document.documentElement,也就是 <html>,它表示整个页面文档。所以文档的宽高,我们自然想到,应该使用 documentElement.scrollWidth/scrollHeight

但这两个属性在除根元素之外的其他元素上使用没有问题,但在根元素上使用就会有点问题,不会得到想象中的结果。在 Chrome/Safair/Opera 中,没有滚动条的情况下,documentElement.scrollHeight 的值可能比 documentElement.clientHeight 的值还要小!

为了得到真实的文档高度,我们需要找出下面列举属性中的最大值:

  1. let scrollHeight = Math.max(
  2. document.body.scrollHeight, document.documentElement.scrollHeight,
  3. document.body.offsetHeight, document.documentElement.offsetHeight,
  4. document.body.clientHeight, document.documentElement.clientHeight
  5. );
  6. alert('文档高度:' + scrollHeight );

为什么要这样?别问了,历史原因,这并不是一段“smart”逻辑。

得到当前滚动距离

普通元素获得滚动状态使用 elem.scrollLeft/scrollTop

那么对应页面呢?很多浏览器提供了 documentElement.scrollLeft/Top,但是 Chrome/Safair/Opera 有 bug(比如 157855106133),所以要使用用 document.body 而非 document.documentElement

幸运的是,我们无需记住这些特异的地方,因为在 window 对象上暴露了两个便捷的属性:pageXOffset/pageYOffset

  1. alert('当前垂直方向滚动的距离:' + window.pageYOffset);
  2. alert('当前水平方向滚动的距离:' + window.pageXOffset);

这两个属性是只读的。

滚动:scrollTo,scrollBy 和 scrollIntoView

窗口尺寸和滚动 - 图4重要

在滚动页面中的元素前,一定要保证要滚动元素已经在 DOM 树中创建了,否则是没有效果的。

普通元素可以通过设置 scorllLeft/scrollTop 的值来滚动元素。

我们也用相同方式,滚动页面:

  • 在 Chrome/Safair/Opera 中:使用 docuement.body.scrollTop/Left

  • 其他浏览器:使用 document.documentElement.scrollTop/Left

所以为了让代码跨浏览器执行良好,我们需要写一些兼容代码。幸运的是,我们有更加简单和通用的解决方法:window.scrollBy()window.scrollTo(pageX, pageY)

  • scrollBy(x, y)方法: 相对于现在的位置就行滚动的。例如,scrollBy(0, 10) 表示再向下滚动 10px 距离。

  • scrollTo(x, y)方法:将文档左上角视为滚动时坐标系中的坐标原点,类似于 scrollLeftscrollTop。例如,我们将页面滚动到初始位置,可以用 scrollTo(0, 0)

这两个方法在所有浏览器中都运行良好。

scrollIntoView

为了完整性,我们再说一个方法:elem.scrollIntoView(top)

调用 elem.scrollIntoView(top) 会让元素 elem 在可视区可见,参数是一个布尔值:

  • top 等于 true 时(默认),元素滚动到窗口顶部,元素上边缘和窗口上边缘对齐。

  • top 等于 false 时,元素滚动到窗口底部,元素下边缘和窗口下边缘对齐。

禁止滚动

有时候我们不希望文档滚动。例如,我们需要在文档上显示一个蒙板,此时只与蒙板里的内容进行交互,不与文档交互。

为了让文档不能滚动,设置 document.body.style.overflow = 'hidden' 就够了。此时页面会固定在当前的滚动位置。

我们可以用相同的方式固定元素,不只是 docuemen.body

这种方式的一个缺点是,滚动条消失后,原来占据的位置就遭释放,导致内容区宽度变大,内容向外扩展了一些。

这看起来有点奇怪,不过我们可以使用 clientWidth 的值来确定滚动条消失前后的宽度,然后给 docuement.body 来个 padding 值来填补滚动条消失的空缺,让文档内容不至于偏移。

总结

几何:

  • 获取文档可视区的宽/高(即内容区宽高,且不包含滚动条):document.documentElement.clientWidth/Height

  • 获取整个文档宽/高:

  1. let scrollHeight = Math.max(
  2. document.body.scrollHeight, document.documentElement.scrollHeight,
  3. document.body.offsetHeight, document.documentElement.offsetHeight,
  4. document.body.clientHeight, document.documentElement.clientHeight
  5. );

滚动:

  • 获取页面当前滚动距离:window.pageXOffset/pageYOffset

  • 滚动页面:

    • window.scrollTo(pageX, pageY):绝对滚动,相对于文档左上角滚动

    • widnow.scrollBy(x, y):相对于当前位置的滚动,

    • elem.scrollIntoView(top):滚动页面让元素可见(与窗口顶部/底部对齐)。

(完)