# 关于窗口宽高

我们可能经常需要迷惑于获取各种宽度和高度问题, 所以统一系统整理一下看看。

对于盒模型和box-sizing, 就暂且按下不表了。我们主要看一下下面几个概念, 心中有个大概的概念最好

# clientWidth

一般我们理解为元素的可见内容宽度;

clientWidth = content + padding

内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。Element.clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。

WARNING

该属性值会被四舍五入为一个整数。如果你需要一个小数值,可使用 element.getBoundingClientRect() (opens new window)

比如下面的例子, container元素的clientWidth = 300 - scrollbar + 40(padding)

<style>
  * {
    padding: 0;
    margin: 0;
  }
  .container {
    width: 300px;
    height: 200px;
    border: 10px solid red
    padding: 20px;
    margin: 30px;
    overflow: auto;
  }
  .content {
    width: 100%;
    height: 600px;
  }
</style>
<div class="container">
  <div class="content"></div>
</div>

盒模型为,宽度只有285是因为Chrome的滚动条默认宽度是15px

# offsetWidth

返回一个元素的布局宽度,一般我们可以理解为一个整体的宽度

offsetWidth = border + padding + scrollbar + content

上述盒模型 offsetWidth = 285 + 15 + 20 * 2 + 10 * 2 = 360

WARNING

这个属性将会 round(四舍五入)为一个整数。如果你想要一个fractional(小数)值,请使用element.getBoundingClientRect() (opens new window).

# scrollWidth、scrollHeight

这两个属性一般就可以理解为盒子内容的宽高

WARNING

  1. 这个属性会进行四舍五入并返回整数,如果你需要小数形式的值,使用element.getBoundingClientRect() (opens new window).

  2. 在实际测试过程中,谷歌获取的 Element.scrollWidth 和 IE,火狐下获取的 Element.scrollWidth 并不相同

因为一些差异的关系,经测试FF会包含padding,Chrome不会, 而且在内容不超出容器高度时,表现也不一致

所以我们简化一下我们的模型,去除容器元素的内外边距,那么就可以理解为内容的宽高, 同时不包含滚动条

<style>
  * {
    padding: 0;
    margin: 0;
  }
  .container {
    width: 300px;
    height: 200px;
    overflow: auto;
  }
  .content {
    width: 100%;
    height: 600px;
  }
</style>
<div class="container">
  <div class="content">hello</div>
</div>

container

s

content

container.scrollWidth = 285

container.scrollHeight = 600

TIP

scrollHeight用在上拉 ·加载更多·的情况中较多

# scrollTop、scrollLeft

两者一样的属性,我们理解scrollTop即可

Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。一个元素的 scrollTop 值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。

获取像素值

const scrollTop = container.scrollTop

设置像素值

container.scrollTop = intValue 

示例代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .container {
        width: 300px;
        height: 200px;
        border: 10px solid red;
        padding: 20px;
        overflow: auto;
      }
      .content {
        width: 100%;
        height: 600px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="content">hello</div>
    </div>
    <script>
      const container = document.querySelector('.container');
      container.addEventListener('scroll', (e) => {
        console.log(container.scrollTop);
      });
    </script>
  </body>
</html>

通过设定scrollTop的值,同样的操作我们可以联想到Element.scrollTo,参考MDN Element.scrollTo (opens new window)

还可以联想到MDN Element.scrollIntoView (opens new window)

# 上拉加载更多

通过上述的知识我们可以把上拉加载更多的实现思路理出来

const threshold = container.scrollHeight - container.scrollTop - container.offsetHeight

threshold 就是还没有滚动出来, 看不见的内容距离,通过监听scroll事件配合节流函数, 我们判断阈值是否小于某个值,如果满足条件则加载更多内容到container

可以参考我借鉴Element实现的无限滚动InfiniteScroll-Vue2-Directives (opens new window)

# Window.innerWidth、innerHeight

innerWidth是Window对象的只读属性,返回以像素为单位的窗口的内部宽度。如果垂直滚动条存在,则这个属性将包括它的宽度。

警告

一般的Element是没有innerWidth属性的。

如果你需要获取除去滚动条和边框的窗口宽度,请使用根元素 <html>clientWidth属性。

# Element.getBoundingClientRect

该方法返回元素的大小以及相对视口的位置。

如果是标准盒子模型,元素的尺寸等于width/height + padding + border-width的总和。如果box-sizing: border-box,元素的的尺寸等于 width/height

返回一个DomRect对象

{
  bottom: 630;
  left: 30;
  right: 315;
  top: 30;
  width: 285;
  height: 600;
  x: 30;
  y: 30;
}

# ScrollX、pageOffsetX ScrollY、pageOffsetY

ScrollX返回文档/页面水平方向滚动的像素值。都是来自于window的只读属性

TIP

pageXOffset 属性是 scrollX 属性的别名

const x = window.scrollX
// window.pageXOffset == window.scrollX; // 总是 true

WARNING

为了跨浏览器兼容性,请使用 window.pageXOffset 代替 window.scrollX。另外,旧版本的 IE(<9)两个属性都不支持,必须通过其他的非标准属性来解决此问题

如果body可以滚动的话, 可以获取body.scrollLeft

const x = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
const y = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

# offsetLeft、offsetTop

HTMLElement.offsetLeft

返回当前元素左上角相对于 HTMLElement.offsetParent (opens new window) 节点的左边界偏移的像素值。

如何理解 HTMLElement.offsetParent, 它返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 tabletdthbody元素。 说重点: 定位

所以我们可以通俗的理解为offsetLeft 返回相对最近的定位的父级元素的偏移。

可以对比Element.style.top

# PageX、PageY

PC上可能是MouseEvent.pageX, 移动端可能是Touch.pageX

这个属性将基于文档的边缘,考虑任何页面的水平方向上的滚动。举个例子,如果页面向右滚动 200px 并出现了滚动条,这部分在窗口之外,然后鼠标点击距离窗口左边 100px 的位置,pageX 所返回的值将是 300。

# clientX、clientY

PC上可能是MouseEvent.clientX, 移动端可能是Touch.clientX

MouseEvent.clientX 是只读属性, 它提供事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)。例如,不论页面是否有水平滚动,当你点击客户端区域的左上角时,鼠标事件的 clientX 值都将为 0

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      html,
      body {
        width: 100%;
        height: 100%;
      }
      .container {
        width: 200%;
        height: 200%;
        background-image: radial-gradient(closest-side,#3f87a6, #ebf8e1,#f69d3c);
      }
    </style>
  </head>
  <body>
    <div class="container"></div>
    <script>
      const container = document.querySelector('.container');
      container.addEventListener('click', (e) => {
        console.log(`pageX=${e.pageX}; clientX=${e.clientX}`);
      });
      container.addEventListener('mousemove', (e) => {
        console.log(`pageX=${e.pageX}; clientX=${e.clientX}`);
      });
    </script>
  </body>
</html>

TIP

这些clientX,pageX的作用可以用于实现签字逻辑等功能

pageX和clientX的区别在于前者考虑滚动, 后者不考虑滚动

上次更新: 1/22/2025, 9:39:13 AM