clientWidth,offsetWidth,scrollWidth你分的清吗

lxf2023-03-14 17:12:01

前言

最近在开发时遇到这样一个需求,一个表单列表报错后,滚动表单到能展示报错元素,做的时候发现clientWidth,offsetWidth,scrollWidth这几个概念自己有时候还是会弄混,所以想写篇文章记录一下。

clientWidth/clientHeight

clientHeight和clientWidth计算时包含元素的content,padding

不包括border,margin和滚动条占用的空间。对于inline的元素这个属性一直是0

clientWidth,offsetWidth,scrollWidth你分的清吗

offsetWidth/offsetHeight

offsetWidth/offsetHeight计算时包含 content + padding + border

不包括margin的元素的高度。对于inline的元素这个属性一直是0

offsetTop/offsetLeft/offsetRight/offsetBottom:

代表元素距离父级元素的相对距离,但是父级元素需要具有relative定位,直到找到body,并且即使元素会被滚动,这个值也不会改变

clientWidth,offsetWidth,scrollWidth你分的清吗

scrollWidth/scrollHeight

scrollWidth/scrollHeight 返回值包含 content + padding + 溢出内容的尺寸,这个只针对dom的子元素出现溢出情况时,才有效果,不然它始终和clientHeight相等

scrollTop

代表在有滚动条时,滚动条向下滚动的距离也就是元素顶部被遮住部分的高度。在没有滚动条时scrollTop==0。

clientWidth,offsetWidth,scrollWidth你分的清吗

getBoundingClientRect()

它返回一个对象,其中包含了left、right、top、bottom四个属性,分别对应了该元素的左上角和右下角相对于浏览器窗口(viewport)左上角的距离

注意:当元素溢出浏览器的视口,值会变成负数。

但是滚动元素是从可视区域的左上角和右下角开始计算,如果想获取滚动元素整体的坐标,需要加上滚动距离

 var X = node.getBoundingClientRect().left+node.scrollLeft;

 var Y = node.getBoundingClientRect().top+node.scrollTop;

一个demo加深印象

有一个列表,当我们输入文段编号,列表会将选中文段滚动到视图中

大概是这样

clientWidth,offsetWidth,scrollWidth你分的清吗

实现思路就是,去拿到选中元素的clientHeightoffsetTop,并和列表的高度区间做比较,计算出元素是在列表视口的内部,还是溢出的视口,如果溢出了视口,那么就回滚。

笔者用react写的,直接附上代码

dom

<div className='container'>
    <div className='scroll' ref={(ref) => (this.scrollRef = ref)}>
        {new Array(15).fill(0).map((_item, index) => (
          <p
            key={index}
            ref={(ref) => (this.pRef[`ref${index}`] = ref)}
            style={{ backgroundColor: Number(el) === index ? 'red' : '' }}
          >{`这是第${index}文段`}</p>
        ))}
    </div>
  </div>

less

.container{
    height: 340px;
    width: 500px;
    margin: auto;
    margin-top: 100px;
    border:1px black solid;
    overflow: hidden;
    padding: 10px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content:space-between;
    flex-direction: column;
}
.scroll{
  height: 300px;
  width: 500px;
  overflow-y: scroll;
  border:1px solid orange;

  p{
    text-align: center;
    font-size:22px;
    color:#9ef64d;
  }

核心方法

const { value } = this.state;
// 滚动视口的高度
const containerHeight = this.scrollRef.clientHeight;
// 滚动视口距离浏览器顶部的距离
const containerOffsetTop = this.scrollRef.getBoundingClientRect().top;

// 选中元素距离浏览器的高度
const { top } = this.pRef[`ref${value}`].getBoundingClientRect();

// needScroll就是元素底部距离滚动容器顶部的距离,再减去20像素,保证出现在视口中间
const needScroll = top - containerOffsetTop - 20;

if (needScroll > containerHeight || needScroll < 0) {
  // 将选中元素放入容器视口中
  const timer = setTimeout(() => {
    this.scrollRef.scrollTop = this.scrollRef.scrollTop + needScroll;
    clearTimeout(timer);
  }, 0);
}

最后

本文整理了clientWidth,offsetWidth,scrollWidth的概念,以及它们所衍生出来的offsetTop,scrollTop的使用,并加上了一个不算复杂的demo,希望能对你有用,当然,如果可以,笔者也希望你能点个赞再走呢

参考链接

www.ruanyifeng.com/blog/2009/0…

developer.mozilla.org/en-US/docs/…