你可能没搞明白的元素位置属性

1,657 阅读3分钟

最近在复习过程中看到了位置相关属性(即client、scroll、offset以及window中的几个位置值),发现这几个随时可能用的属性一直没记牢过。在网上搜索几篇文章看下来后却发现越来越迷糊,主要争论在clientHeight上:

  • 一些说clientHeight是元素的可见高度
  • 一些说clientHeight是元素的完整高度 本着实践出真知的原则,干脆自己动手研究,顺便记录分享

直接说结论

以下的content,指标准盒模型的content部分的宽度或高度(具体范围查看文末图示);
padding、border、滚动条,指各自的宽度(厚度),值不对称时将*2改为相加;
内容,指子元素的总渲染宽度或高度。

  1. client系列(均为只读)
    • clientHeightclientWidth

      content + padding*2 - 滚动条 即盒模型border内矩形的宽高

    • clientLeftclientTop

      border [+ 滚动条] 即border的宽度。rtl模式下左边框挨着垂直滚动条,则需要加上滚动条

  2. scroll系列(scrollTop、scrollLeft可写)
    • scrollHeightscrollWidth

      padding*2 + 内容 - 滚动条 即border内的宽高,包括溢出不可见部分

    • scrollLeftscrollTop值等于

      文档流起点方向上被滚动条隐藏的 内容+padding 大小 rtl模式下水平滚动条右边为起点0,滚动后scrollLeft为负值。也就是正常模式下,scrollLeft是被滚动隐藏的左边部分内容+padding;rtl模式下为右边部分被隐藏的内容+padding,且为负值

  3. offset系列(均为只读)
    • offsetParent

      指向最近包含该元素的定位元素或者最近的 table,td,th,body元素。当元素的 style.display 设置为 none 时,offsetParent 返回 null

    • offsetHeightoffsetWidth

      border*2 + padding*2 + content 即元素被完整撑开后的宽高

    • offsetLeftoffsetTop

      元素border外框线,到offsetParent的margin外框线的距离 offsetParent为null时,值为0

  4. window系列(均为只读)
    • innerHeightinnerWidth

      视口的宽高,包括滚动条

    • outerHeightouterWidth

      整个浏览器的宽高,包括浏览器的菜单栏、标题栏等

    • scrollX(别名pageXOffset)与scrollY(别名pageYOffset

      与scrollLeft、scrollTop相似,只是目标是整个窗口,且需要把margin计算在内 考虑兼容性,建议使用别名;rtl模式下同scroll系列一样为负值

  5. 行内元素的client系列与scroll系列值均为0,offset系列值正常计算
  6. 除了scrollLeft、scrollTop在使用显示比例缩放的系统上可能会是一个小数,其他属性均为四舍五入的整数。

建议使用Element.getBoundingClientRect()获取位置属性,结果为相对于页面文档流起点方向的小数(MDN中说现代浏览器对于返回结果能够修改,但测试发现修改没有效果)

如果获取元素是否出现在页面中,还可以使用IntersectionObserver,更方便且性能更佳,在我另一篇文章里有相关介绍

image.png

比较有意思的点

  1. 无论是content-box还是border-box,滚动条始终抢用content像素位置。padding在滚动条内侧,大小不变;
  2. 浏览器计算位置数值会把被溢出隐藏的padding部分提到content盒子内计算(看下面的图)
  3. 谷歌浏览器滚动条默认17px,body的margin默认8px

浏览器开发者工具中显示的数值与上述属性得到的值一致,但页面显示中因为被滚动条挤压,content实际像素可能小于计算数值

  • box-sizing=content-box时,content等于width或height - 滚动条,开发者工具指示区域实际像素等于开发者工具显示数值;
  • box-sizing=border-box时,content等于width - border*2 - padding*2 - 滚动条,开发者工具指示区域实际像素小于开发者工具显示数值。

box-sizing = content-box 时:

content-box

boxsizing = border-box 时:

image.png