前言

最近在开发时遇到这样一个需求,一个表单列表报错后,翻滚表单到能展现报错元素,做的时候发现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/…