你必须知道的 clientWidth, offsetWidth, scrollWidth.

前言: 在公司移动端项目中,我需要十分频繁的和 DOM 元素的各种 widthheight 打交道。但是这里有这么多关于 width 的属性,它们之间的区别到底体现在哪里?这是我刚刚接触移动端项目十分头疼的一个问题。经过几天的查阅,终于搞明白了这几个的不同之处,特来分享。

tips: 本文意指让你自己通过联想记牢这几个属性的区别,而不是单纯地表达它们概念上的不同,故篇幅会较长,还希望读者耐下心细细品读☕️。

一. 前期准备

  1. 在学习这几个概念之前,我强烈建议你用你喜欢的框架或者工具去简单写一个
    元素来更深刻的体会我接下来讲解的内容。不用特别复杂,十分简单的,带有宽度高度的 div 即可。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第1张图片

    在页面上的效果如下。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第2张图片
  2. 接下来你需要进入到开发者页面,选中刚刚我们创造的这个 div 元素。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第3张图片
  3. 然后你需要从右边的选项卡中找到 Properties(dom 属性) 这一项中筛选出带有 width 的几个属性。



    接下来你将会频繁的看到这三个属性的变化。

二. clientWidth

  1. 三个属性中我们先对 clientWidth 初步讲解一下。
  2. 我相信大家初次去 MDN 查阅这个 Css 属性的时候,一定看到过下面这张图。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第4张图片
  3. 初次看到这张图,感觉它什么都讲了,但是又好像不知道它到底想表达什么。如果你也有这样的疑惑,本文将带你一步步去体会它到底想表达什么。
  4. 首先切忌死记硬背属性名,我们需要十分明确的知道这个属性名为什么叫 clientWidth 而不是叫 小猪佩奇Width。下面是这个单词的翻译,我认为 client 这个单词在这里语境中最佳的翻译应该为---"客户端,用户端"

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第5张图片
  5. ok,我知道你可能还是有疑惑,别着急,我们一步一步来。经过上面 width 属性搜索结果可知,聪明的你可能已经发现 clientWidth 的值不就是我们给这个 box 设置的 width 的属性值 100 吗?

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第6张图片
  6. 是的,对了,但是不完全对。这里更好的说法应该是:“按照我们现在的布局和样式,clientWidthwidth是相等的。”对,只能称作它们的是相等的,而不能说这两个属性是全等的。
  7. 那这个 clientWidth 到底该怎么去理解呢?“客户端的宽度用户端的宽度?”总感觉哪里怪怪的。
  8. 在这里我先抛出一个我个人认为比较贴切的说法:“clientWidth 指的是一个 dom 元素中,当前状态下可以被用户即时看到的宽度(理解为可视区域的宽度或许也可以)。
  9. ”什么意思?假设你现在面前放着一个空的正方体的快递盒子。里面填充了一个大小恰好一模一样的黑色正方体铁块。那么你目前看到的这个铁块的宽度就称作快递盒子(box)的 clientWidth
  10. 现在我们把铁块取出来,得到了一个空的盒子对吧?现在我在盒子周围贴上了一圈10厘米宽的白色泡沫板。然后又找来了一个小一号的,但是又恰好可以一起塞进箱子里的铁块。体现到我们的例子上就是 box 加上了 10px 的 border 属性。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第7张图片

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第8张图片

    然后我们再去 Properties 选项卡查看这三个属性。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第9张图片
  11. 我们发现 clientWidth 的宽度减少了 20px。(我们不是只设置了 10px 吗?怎么一下子减少了 20px 呢?我希望你不要忘了,border 如果没指定具体方向的话,它会在上下左右都设定一份...就像你在快递盒子四个边都塞了一份泡沫板)
  12. 此时你能看到的铁块的宽度就是 clientWidth,我们好像可以得出一个结论,clientWidth = width- 左border - 右border。不对,不能这么早下结论,我们好有影响一个重要属性 padding。那么现在我们给这个盒子添加一个 10px 的 padding测试一下。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第10张图片

    好像确实没有影响 clientWidth 的宽度,看来我们的这个结论是具有一定的依据的。
    你必须知道的 clientWidth, offsetWidth, scrollWidth._第11张图片
  13. 其实在这里我更想表达的是,我们在理解 padding 这个属性的时候,我们其实可以把它理解为内容区的一部分。因为 padding 属性其实也是受到了 bg-color(background-color) 的影响,而 border 属性是没有受到 bg 影响而变为黑色的。这也是为什么 padding 没有 padding-color 这一属性的原因,因为它本身是被当成了内容区的一一部分,它的颜色是随着内容区(也就是 bg-color) 而改变的原因。
  14. 这时候我们再去看这张图,这里 padding 为什么有颜色?这其实是 MDN 为了更好的去表现出 padding 属性而“特意”添加上了不同颜色的。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第12张图片

    所以, clientWidth 这一属性更像是描述用户可以直接看到的内容区域。

三. offsetWidth

  1. 关于 offsetWidth,其实 MDN 有这样一句话能够很完美的表现出它想表达的意思。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第13张图片
  2. 它的概念其实非常非常简单,就是在 box-sizing:border 的时候 offsetWidth 其实就等于 dom 元素的 width。不知道你是否遗忘了 box-sizing:content 这个标准盒子模型的概念。让我们切换一下 box-sizing 的属性,变为 content 来看看这个属性值有什么变化。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第14张图片

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第15张图片

    可以很清楚的看到,在 box-sizing:conetent 的时候 offsetWidth= width + 左border + 右border + 左padding + 右padding

    这是因为在 content-box 的情况下,我们设置的 dom 元素的宽度其实仅仅只是内容区的宽度。我们设置的 border 和 padding 都是由内容区向外扩张。
border-box 下设置的 borderpadding 都是由盒子向内部收缩来给 border 或者 padding “腾地方”。
  1. offsetWidth 这个属性是为了表达了盒子的真实物理宽度。所以它的计算方式会根据 box-sizing 的不同而不同。
  2. 在这里你可能会好奇,为什么 clientWidth 也跟着变了?其实根据我们上面讲的:“padding 其实是算作特殊的内容区”来分析,很容易就可以想到 clientWidth= 100px+ 10px + 10px, 所以就等于了 120px。

四. scrollWidth

  1. 为什么在上面我压根没提这个属性呢?因为这个属性的触发条件很特殊,它只有在特殊的场景下才能体现出和 clientWidth 的区别。(tips:它确实是需要对标 clientWidth 的,先不要疑惑,接着往下看你就明白为什么对标的是 clientWidth 而不是 offsetwidht 了。)
  2. 那这个“特殊场景”指的是什么呢?其实你应该非常熟悉----overflow
  3. ok,那我们怎么创建这个特殊场景呢?其实非常非常简单,你在这个 100px 宽度的盒子里多打几个字就可以。 我们简单设置一下字体的颜色和大小。注意:这里我们已经把 box-sizing 切换成了 border-box,这里为了表现溢出,我们需要设置一个特殊的属性 white-space:nowrap

    
    
    
    ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4ae5bf5960248fba5bae33d8c9bd11e~tplv-k3u1fbpfcp-watermark.image?)
  4. 很清楚的看到,由于我们的字体宽度过大,导致盒子塞不下,但是我们又没有设置溢出场景的处理方法,就导致了现在的样子。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第16张图片
  5. 我想你已经迫不及待的去 Properties 去查看这几个属性了,但是你有可能一脸疑惑的回来。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第17张图片

    什么情况,这 scrollWidth 也没变啊!我怀疑你在骗我。
  6. 其实从 scroll 这个词我们猜或许也能猜到什么。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第18张图片

    既然和“滚动”有关,那么现在页面发生滚动了吗?没有,怎么才可以滚动呢?没错 overflow-auto(其它值也一样,只不过 auto 方便一点)。
  7. 设置了 overflow 的处理后,我们现在页面应该是下面这个样子。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第19张图片

    我们再去查看这三个属性,你会发现 scrollWidth 已经变了。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第20张图片
  8. 那 390 这个数值是怎么来的呢?这个其实数值要根据你实际项目造成溢出的原因来分析的。但是 scrollWidth 这个属性实际上代表的是“dom元素内容区的真实宽度”。什么意思呢?让我们先把 overflow-auto 属性删除,恢复成它原来溢出的样子。

    你必须知道的 clientWidth, offsetWidth, scrollWidth._第21张图片
  9. 然后我们需要通过一些“特殊的手法”去测量一下这个值是体现在哪里。


  10. 实际上就是文字区域+ padding 区域的宽度。说的直白一点,其实就是内容区(content)。(我再强调一下,你可以把 padding 理解为特殊的内容区
  11. 没错,到这里我想你大概已经明白了,实际上 scrollWidth 代表的就是内容区的真实宽度。在这里我们就需要把 clientWidth 拎出来讲一下了。同样是表达内容区宽度的。
1.在内容区没有发生溢出的情况下,scrollWidth = clientWidth 因为它们都是代表内容区的宽度。

2.在内容区发生了溢出,并且设置了 overflow-scroll 之类的属性的情况下,clientWidth 代表dom 当前状态下,实际上展示在可视区域的内容区(content)的宽度。,而 scrollWidth 则代表了真实的 内容区的宽度,包括了那些没有展现在用户面前的,需要滚动才可以看到的内容的宽度。这时候 scrollWidth= clientWidth + 溢出的内容区的宽度

结语

从学习这几个属性的过程中才真正体会到:“看一千遍文档,不如自己动手实践一遍”的道理。还希望大家也可以跟着自己敲一敲,真的不难。与君共勉才是我写这篇文章的初衷~

你可能感兴趣的:(css3前端)