offset、client、scroll相关

263 阅读5分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

offsetParent

offsetParent 是一个只读属性,返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 table,td,th,body元素。当元素的 style.display 设置为 "none" 时,offsetParent 返回 null。

offsetTop 和 offsetLeft 都是相对于其内边距边界的。

参考文献offsetParent MDN

offset系列

offsetTop、offsetLeft

只读属性,它返回当前元素相对于其 offsetParent元素的顶部(左部)内边距的距离。

举个块级元素的栗子:

<div className='slide' ref={parentRef}>
    <div id='child' ref={childRef}></div>
</div>
.slide {
  width: 200px;
  height: 500px;
  overflow: scroll;
  margin: 30px auto;
  position:relative;
  border: 1px solid #000;
}

#child {
  width: 50px;
  height: 700px;
  margin: 30px auto;
  /* padding-top: 100px; */
  /* padding-left: 20px; */
  /* box-sizing: border-box; */
  background-color: aqua;
}

如图所示: 我们给child设置了margin:30px auto;所以child的offsetTop:30px,offsetLeft:75px [(200 - 50) / 2],offsetWidth = width(50px),offsetHeight(700px)

如果我们给child设置了padding就会有不同

如图所示: 我们给child设置了padding-top:100px;padding-left:20px;,padding-top没有影响offsetTop的值,但offsetHeight的值改变为800px(height + padding),padding-left使offsetWidth的值变成了70px,offsetLeft的值变成了65px[(200 - width50px - padding20px) / 2]

如果我们设置了box-sizing:border-box结果和第一种一样

总结:

  1. 需要注意child的offset属性之所以基于slide计算,是因为我们给slide设置了相对定位,child的offsetParent是slide
  2. padding会影响元素的offsetWidth=盒子的宽度=width+padding=70px,我们设置的宽度只是width的值,所以也会影响offsetLeft,padding会影响元素的offsetHeight,但是不会影响offsetTop
  3. 我们设置了box-sizing:border-box,盒子的宽度=padding+width=50px,我们设置的宽度就是width+padding,所以和情况1一致。

offsetWidth、offsetHeight

offsetWidth只读属性,返回一个元素的布局宽度。 offsetWidth = padding + border + width + scrollbar(竖直方向滚动条)

offsetHeight只读属性,返回一个元素的布局高度。 offsetHeight = padding + border + height + scrollbar (水平方向滚动条)

举个行内元素的栗子:

<div className='slide' ref={parentRef}>
    <div id='child' ref={childRef}></div>
    <div id='spanPartent'>
      <span id={'inline-1'}>你好你好</span>
      <span id={'inline-2'}>这是第二个span元素,这是第二个span元素</span>
    </div>
  </div>
.slide {
  width: 200px;
  height: 500px;
  overflow: scroll;
  margin: 30px auto;
  position:relative;
  border: 1px solid #000;
}

#child {
  width: 50px;
  height: 700px;
  margin: 30px auto;
  padding-top: 100px;
  padding-left: 20px;
  box-sizing: border-box;
  background-color: aqua;
}

#spanPartent {
  border: 1px solid green;
}

#inline-1 {
  border: 1px solid blue;
  margin-left: 30px;
}

#inline-2 {
  border: 1px solid red;
  margin-left: 20px;
  padding-top: 20px;
}
31 'span-1offsetLeft'
117 'span-2offsetLeft'
760 'span-1offsetTop'
740 'span-2offsetTop'
66 'span-1offsetWidth'
197 'span-2offsetWidth'
25 'span-1offsetHeight'
90 'span-2offsetHeight'

如图所示:

我们可以直观的看出来span元素的计算方式,padding会影响span元素的高度,进而会影响offsetHeight和offsetTop的值。span1和span2的值也是不一样的。

client系列

clientTop 表示一个元素的上边框宽度,是只读属性。 clientLeft 表示一个元素的左边框的宽度,以像素表示。如果元素的文本方向是从右向左(RTL, right-to-left),并且由于内容溢出导致左边出现了一个垂直滚动条,则该属性包括滚动条的宽度。clientLeft 不包括左外边距和左内边距。clientLeft 是只读的。

  1. 简单来说是我们设置的border的宽度,但是如果你是用outline或者box-shadow设置的边框是不会计算在内。
  2. 行内元素是不存在client属性的,属性值都为0。
  3. clientWidth和clientHeight的值是padding+width/height的值不包括border,怪异盒模型和IE盒模型都如此。
#child {
  width: 50px;
  height: 700px;
  margin: 30px auto;
  padding-top: 100px;
  padding-left: 20px;
  box-sizing: border-box;
  border: 5px solid red;
  background-color: aqua;
}

5 'clientTop'
5 'clientLeft'
40 'clientWidth'
690 'clientHeight'
#child {
  width: 50px;
  height: 700px;
  margin: 30px auto;
  padding-top: 100px;
  padding-left: 20px;
  border: 5px solid red;
  background-color: aqua;
}

0 'clientTop'
0 'clientLeft'
50 'clientWidth'
700 'clientHeight'

scroll系列

scrollTop 可设置的 scrollTop 值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0,我们可以理解为滚动条顶部到其设置了overflow:scroll的父元素的边框位置的高度。

scrollWidth 这个只读属性是元素内容宽度的一种度量,包括由于 overflow 溢出而在屏幕上不可见的内容。计算规则和clientWidht相同,如果没有溢出滚动则值等同于clientWidth。

scrollHeight MDN

scrollTo

scrollTo是给设置了overflow;scroll的元素调用的方法,可以控制元素滚动到相应的位置 element.scrollTo(x-coord, y-coord) element.scrollTo({ top: 100, left: 100, behavior: 'smooth' });

scrollIntoView

scrollIntoView MDN scrollIntoView是给设置了overflow;scroll元素的子元素调用的方法

  <div className='slide' ref={parentRef}>
    <div id='child' ref={childRef}></div>
  </div>
.slide {
  width: 200px;
  height: 500px;
  overflow: scroll;
  margin: 30px auto;
  position:relative;
  border: 1px solid #000;
}

#child {
  width: 50px;
  height: 2000px;
  margin: 30px auto;
  background-color: aqua;
}
parentRef.current.scrollTo(0, 30)
childRef.current.scrollIntoView(true)
// 以上这两种方法等同

scrollBy

滚动的位置是相对的,也就是说第一次滚动到(100, 100)的位置第二次再滚动就是以(100,100)为起点滚动,就会到(200,200)的位置 与scrollTo不同,scrollTo每次都是绝对位置,每次都是从(0,0)开始计算的。