2022 年 6 月 26 日

窗口大小和滚动

我们如何找到浏览器窗口的宽度和高度?我们如何获取文档的完整宽度和高度,包括滚动出的部分?我们如何使用 JavaScript 滚动页面?

对于此类信息,我们可以使用根文档元素 document.documentElement,它对应于 <html> 标记。但还需要考虑其他方法和特殊情况。

窗口的宽度/高度

要获取窗口宽度和高度,我们可以使用 document.documentElementclientWidth/clientHeight

例如,此按钮显示您的窗口高度

而不是 window.innerWidth/innerHeight

浏览器还支持 window.innerWidth/innerHeight 等属性。它们看起来像我们想要的内容,那么为什么不使用它们呢?

如果存在滚动条,并且它占据了一些空间,则 clientWidth/clientHeight 会提供不包含它的宽度/高度(减去它)。换句话说,它们返回文档可见部分的宽度/高度,可用于内容。

window.innerWidth/innerHeight 包含滚动条。

如果存在滚动条,并且它占据了一些空间,那么这两行显示不同的值

alert( window.innerWidth ); // full window width
alert( document.documentElement.clientWidth ); // window width minus the scrollbar

在大多数情况下,我们需要可用窗口宽度才能在滚动条内绘制或定位某些内容(如果有),因此我们应该使用 documentElement.clientHeight/clientWidth

DOCTYPE 很重要

请注意:当 HTML 中没有 <!DOCTYPE HTML> 时,顶级几何属性可能会有所不同。可能会出现奇怪的事情。

在现代 HTML 中,我们应该始终编写 DOCTYPE

文档的宽度/高度

从理论上讲,由于根文档元素是 document.documentElement,并且它包含所有内容,因此我们可以将文档的完整大小测量为 document.documentElement.scrollWidth/scrollHeight

但是对于该元素,对于整个页面,这些属性无法按预期工作。在 Chrome/Safari/Opera 中,如果没有滚动,则 documentElement.scrollHeight 甚至可能小于 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);

为什么这样?最好不要问。这些不一致之处来自古代,而不是“智能”逻辑。

获取当前滚动

DOM 元素在其 scrollLeft/scrollTop 属性中具有其当前滚动状态。

对于文档滚动,document.documentElement.scrollLeft/scrollTop 在大多数浏览器中都可以使用,除了基于 WebKit 的旧浏览器,如 Safari(错误 5991),其中我们应该使用 document.body 而不是 document.documentElement

幸运的是,我们根本不必记住这些特性,因为滚动可在特殊属性 window.pageXOffset/pageYOffset 中使用

alert('Current scroll from the top: ' + window.pageYOffset);
alert('Current scroll from the left: ' + window.pageXOffset);

这些属性是只读的。

也可作为window属性scrollXscrollY

出于历史原因,这两个属性都存在,但它们是相同的

  • window.pageXOffsetwindow.scrollX的别名。
  • window.pageYOffsetwindow.scrollY的别名。

滚动:scrollTo、scrollBy、scrollIntoView

重要

要使用 JavaScript 滚动页面,其 DOM 必须完全构建。

例如,如果我们尝试使用<head>中的脚本滚动页面,它将不起作用。

可以通过更改scrollTop/scrollLeft来滚动常规元素。

我们可以使用document.documentElement.scrollTop/scrollLeft对页面执行相同的操作(Safari 除外,在 Safari 中应使用document.body.scrollTop/Left)。

或者,还有一个更简单、更通用的解决方案:特殊方法 window.scrollBy(x,y)window.scrollTo(pageX,pageY)

  • 方法scrollBy(x,y)相对于其当前位置滚动页面。例如,scrollBy(0,10)将页面向下滚动10px

    下面的按钮演示了这一点

  • 方法scrollTo(pageX,pageY)将页面滚动到绝对坐标,以便可见部分的左上角具有相对于文档左上角的坐标(pageX, pageY)。这就像设置scrollLeft/scrollTop

    要滚动到最开始,我们可以使用scrollTo(0,0)

这些方法对所有浏览器的工作方式相同。

scrollIntoView

为了完整起见,我们再来介绍一个方法:elem.scrollIntoView(top)

调用elem.scrollIntoView(top)将滚动页面以使elem可见。它有一个参数

  • 如果top=true(这是默认值),则页面将滚动以使elem出现在窗口顶部。元素的上边缘将与窗口顶部对齐。
  • 如果top=false,则页面将滚动以使elem出现在底部。元素的下边缘将与窗口底部对齐。

下面的按钮将页面滚动到位于窗口顶部的位置

此按钮将页面滚动到位于底部的位置

禁止滚动

有时我们需要让文档“不可滚动”。例如,当我们需要用一个需要立即注意的大消息覆盖页面时,我们希望访问者与该消息交互,而不是与文档交互。

要让文档不可滚动,只需设置 document.body.style.overflow = "hidden"。页面将“冻结”在其当前滚动位置。

试试看

第一个按钮冻结滚动,而第二个按钮释放滚动。

我们可以使用相同技术冻结其他元素的滚动,而不仅仅是 document.body

该方法的缺点是滚动条消失了。如果它占用了空间,那么该空间现在是空闲的,并且内容“跳动”以填充它。

这看起来有点奇怪,但是如果我们在冻结前后比较 clientWidth,则可以解决此问题。如果它增加了(滚动条消失了),则在滚动条的位置向 document.body 添加 padding 以保持内容宽度相同。

总结

几何

  • 文档可见部分的宽度/高度(内容区域宽度/高度):document.documentElement.clientWidth/clientHeight

  • 整个文档的宽度/高度,包括滚动出的部分

    let scrollHeight = Math.max(
      document.body.scrollHeight, document.documentElement.scrollHeight,
      document.body.offsetHeight, document.documentElement.offsetHeight,
      document.body.clientHeight, document.documentElement.clientHeight
    );

滚动

  • 读取当前滚动:window.pageYOffset/pageXOffset

  • 更改当前滚动

    • window.scrollTo(pageX,pageY) – 绝对坐标,
    • window.scrollBy(x,y) – 相对于当前位置滚动,
    • elem.scrollIntoView(top) – 滚动以使 elem 可见(与窗口顶部/底部对齐)。
教程地图

评论

评论前请阅读此内容…
  • 如果您有改进建议 - 请 提交 GitHub 问题 或拉取请求,而不是评论。
  • 如果您无法理解文章中的内容,请详细说明。
  • 要插入少量的代码,请使用 <code> 标记,对于多行代码,请将其包装在 <pre> 标记中,对于超过 10 行的代码,请使用沙盒 (plnkrjsbincodepen…)