不背概念,通过测试直观推导 clientWidth、offsetWidth 、scrollWidth等等的含义

521 阅读5分钟

前言

大家好,我是抹茶。 长期以来一直没有彻底弄懂clientWidth、offsetWidth、scrollWidth的具体含义,查资料都总是遇到每个字我都认识,但是组合在一起不知道他想表达什么的困境。所以我决定不嗑资料了,而是自己写测试demo,去推导和理解各个字段的含义。

术语解释

因为形如width + paddingLeft + paddingRight的方式要记的信息点太多,所以笔者直接采用content-boxpadding-boxborder-boxmargin-box的方式进行记忆。

content-box

content-box为box-sizing:content-box时width占据的大小

image.png

padding-box

padding-box为padding及以内包裹的内容的大小

image.png

border-box

border-box为border及以内包裹的内容的大小

image.png

margin-box

margin-box为margin及以内包裹的内容的大小

image.png

测试过程

1.clientWidth、offsetWidth、scrollWidth

  • div没有横向滚动条的时候

offsetWidth最大,为盒子整体占据的屏幕大小(不含margin)
clientWidth和scrollWidth为padding-box的大小

测试条件:

  1. 屏幕宽度:1016px
  2. 父级盒子的margin-left:8px,margin-right:8px;

测试结果:

  • 有竖向滚动条的:
    • clientWidth: 965
    • offsetWidth: 1000
    • scrollWidth: 965

image.png

  • 无竖向滚动条的:
    • clientWidth: 980
    • offsetWidth: 1000
    • scrollWidth: 980

image.png

对比可得,clientWidth和scrollWidth不包含滚动条的宽度(980和965之间的差距就是竖向滚动条的宽度),clientWidth和scrollWidth为padding-box的宽度(根据盒模型宽度推断得出)。

offsetWidth为border-box的宽度 + 竖向滚动条的宽度。

image.png
  • div有横向滚动条的时候

有溢出的时候,scrollWidth最大,为盒子内容的最小宽度。

没有溢出时保持offsetWidth是border-box的宽度,clientWidth和scrollWidth为padding-box的宽度

image.png

2.clientHeight、offsetHeight、scrollHeight

  • 测试条件
  height:150px;
  padding:20px;
  border:10px solid xxx;
  • 测试结果
    • 有竖向滚动条时
      scrollHeight最大,为内容的高度

    • 没有滚动条时
      offsetHeight最大,为border-box的高度,clientHeight和scrollHeight为padding-box的高度

image.png

3.clientLeft、offsetLeft

测试条件

// 父级盒子
padding20px;
border:10px solid xxx;

//子盒子
padding:10px;
border: 5px solid xxx;

// 浮动元素子盒子
padding10px;
border:6px solid xxx;

image.png

image.png

结合图中信息可得,clientLeft为div盒子的border-left的宽度,offsetWidth为到屏幕最左边的距离。

4.clientTop、offsetTop

测试条件

// 父级盒子
padding20px;
border:10px solid xxx;

//子盒子
padding:10px;
border: 5px solid xxx;

// 浮动元素子盒子
padding10px;
border:6px solid xxx;

image.png 从图中信息可得,clientTop是border-top的宽度,offsetTop是到距离(0,0)的高度。

5. scrollLeft和scrollRight

scrollLeft为div出现横向滚动条时,滚动条到左边的距离。
scrollRight为div出现竖向滚动条时,滚动条到上边的距离。

image.png

在没有出现滚动条的时候,或者滚动条移动到最左边和最上边,scrollLeft和scrollTop都为0。

image.png

总结

首先,clientXXX相关的宽高指的是padding-box的宽高,而offsetXXX相关的宽高指的是border-box的宽高+对应方向的滚动条的宽度

scrollWidth和scrollHeight根据出现滚动条与否差别较大,没有出现滚动条的时候scrollWidth == clientWidthscrollHeight == clientHeight,在出现滚动条的时候,他们等于内容的最小宽度。

offsetLeft和offsetTop指的是到左上角(0,0)的距离,而clientLeft和clientTop分别指的是左边和右边的border的宽度。

scrollLeft和scrollTop表示的是滚动条的移动距离,如果没有滚动条,或者滚动条位于最左边或最上边时,scrollLeft和scroll都为0。

测试源码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        margin: 0;
        box-sizing: content-box;
      }
      /* 滚动盒子 */
      .scroll-box {
        height: 150px;
        overflow: auto;
        padding: 20px;
        margin: 8px;
        border: 10px solid rgb(56, 18, 179);
      }

      .scroll-item {
        width: 600px;
        height: 200px;
        padding: 10px;
        border: 5px solid purple;
        background-color: skyblue;
      }

      /* 浮动盒子 */
      .float-box {
        margin: 8px;
        margin-top:20px;
        height: 150px;
        padding: 20px;
        border: 10px solid palegreen;
      }
      .float-item {
        width: 100px;
        height: 100px;
        padding:5px;
        border:6px solid purple;
        float: left;
        background-color: rgb(63, 224, 224);
      }

      /* 绝对定位 */
    .relative-box{
      position:relative;
      margin: 8px;
      margin-top:20px;
      height:150px;
      padding:20px;
      border: 10px solid purple;
    }

    .absolute-item{
      position:relative;
      margin-top:20px;
      width:50%;
      height:50%;
      padding:10px;
      border: 5px solid skyblue;
    }

    .flex-box{
      display: flex;
      margin: 8px;
      margin-top:20px;
      height:150px;
      padding:20px;
      border:10px solid orange;
      justify-content: space-between;
      align-items: center;
    }

    .flex-item{
      width:100px;
      height:100px;
      padding:10px;
      border:5px solid seagreen;
    }

    </style>
  </head>
  <body>
    <div class="scroll-box" id="scroll-parent"  onclick="computedStyle('scroll-parent','滚动布局的父元素')">
      <div class="scroll-item" id="scroll-child" onclick="computedStyle('scroll-child','滚动布局的子元素',event)">滚动布局</div>
    </div>

    <div class="float-box" id="float-parent" onclick="computedStyle('float-parent','浮动布局的父元素')">
      <div class="float-item" id="float-child" onclick="computedStyle('float-child','浮动布局的子元素',event)">浮动元素</div>
    </div>

    <div class="relative-box" id="relative-parent" onclick="computedStyle('relative-parent','绝对定位的父元素')">
      <div class="absolute-item" id="absolute-child" onclick="computedStyle('absolute-child','绝对定位的子元素',event)">绝对定位</div>
    </div>

    <div class="flex-box" id="flex-parent" onclick="computedStyle('flex-parent','浮动布局的父元素')">
      <div class="flex-item" id="flex-child-1" onclick="computedStyle('flex-child-1','flex一号盒子',event)">flex一号盒子</div>
      <div class="flex-item" id="flex-child-2" onclick="computedStyle('flex-child-2','flex二号盒子',event)">flex二号盒子</div>
    </div>

    
  </body>
  <script type="text/javascript">
    // 如果你需要处理多个元素,可以定义一个函数来获取这些属性
    function getElementProperties(id) {
      const element = document.getElementById(id);
      if(!element) {
        console.log('找不到,id:'+id);
        return;
      }


      // 获取元素的尺寸和位置
      const offsetWidth = element.offsetWidth;
      const offsetHeight = element.offsetHeight;
      const offsetTop = element.offsetTop;
      const offsetBottom = element.offsetBottom;
      const offsetLeft = element.offsetLeft;
      const offsetRight = element.offsetRight;

      // 获取滚动相关属性
      const scrollWidth = element.scrollWidth;
      const scrollHeight = element.scrollHeight;
      const scrollTop = element.scrollTop;
      const scrollBottom = element.scrollBottom;
      const scrollLeft = element.scrollLeft;
      const scrollRight = element.scrollRight;

      // 获取元素边框和客户区大小
      const clientTop = element.clientTop;
      const clientBottom = element.clientBottom;
      const clientLeft = element.clientLeft;
      const clientRight = element.clientRight;
      const clientWidth = element.clientWidth;
      const clientHeight = element.clientHeight;

      return {
        // offset: {
        //   offsetWidth,
        //   offsetHeight,
        //   offsetTop,
        //   offsetBottom,
        //   offsetLeft,
        //   offsetRight,
        // },
        // client: {
        //   clientWidth,
        //   clientHeight,
        //   clientTop,
        //   clientBottom,
        //   clientLeft,
        //   clientRight,
        // },
        // scroll: {
        //   scrollWidth,
        //   scrollHeight,
        //   scrollTop,
        //   scrollBottom,
        //   scrollLeft,
        //   scrollRight,
        // },
        // width: {
        //   offsetWidth,
        //   clientWidth,
        //   scrollWidth,
        // },
        // height: {
        //   offsetHeight,
        //   clientHeight,
        //   scrollHeight,
        // },
        // top: {
        //   offsetTop,
        //   clientTop,
        //   scrollTop,
        // },
        //  bottom:{
        //   offsetBottom,
        //   scrollBottom,
        //   clientBottom
        // },
        // left: {
        //   offsetLeft,
        //   clientLeft,
        //   scrollLeft,
        // },
        right: {
          offsetRight,
          clientRight,
          scrollRight,
        },
       
      };
    }

    function computedStyle(id,name,event) {
     
      // 使用该函数
      const properties = getElementProperties(id);
      console.group(name);
      console.log(properties);
      console.groupEnd();
     
      console.log('\n');
      
      if (event) {
       
        event.stopPropagation(); // 阻止事件冒泡
      }
    }
  </script>
</html>