offset、scroll 和 client

225 阅读8分钟

offsetParent (找爸爸)

  1. HTMLElement.offsetParent 是一个只读属性,返回一个指向最近的(指包含层级上的最近)包含该元素定位元素或者最近的 table,td,th,body 元素。

  2. 当元素的 style.display 设置为 "none" 时,offsetParent 返回 null

  3. offsetParent 很有用,因为 offsetTopoffsetLeft 都是相对于其内边距边界的。

浏览器兼容性

  1. Webkit(Chrome、Safari) 中,如果元素为隐藏的(该元素或其祖先元素的 style.display"none"),或者该元素的 style.position 被设为 "fixed",则该属性返回 null

  2. IE 9 (Trident)中,如果该元素的 style.position 被设置为 "fixed",则该属性返回 null。(display:none 无影响。)

    offsetWidth

    💙 width + padding + border

  3. HTMLElement.offsetWidth 是一个只读属性,返回一个元素的布局宽度。

  4. 一个典型的(译者注:各浏览器的offsetWidth可能有所不同

  5. offsetWidth是测量包含元素的边框(border)、水平线上的内边距(padding)、竖直方向滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值。

offsetHeight

💙 height+ padding + border

  1. HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数

  2. 通常,元素的offsetHeight是一种元素CSS高度的衡量标准,包括元素的边框、内边距和元素的水平滚动条(如果存在且渲染的话),不包含:before或:after等伪类元素的高度

  3. line-height 会影响 offsetHeight

  4. 对于文档的body对象,它包括代替元素的CSS高度线性总含量高。浮动元素的向下延伸内容高度是被忽略的。

  5. 如果元素被隐藏(例如 元素或者元素的祖先之一的元素的style.display被设置为none),则返回 0

offsetTop

💙 offsetTop = top +marginTop

HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离。

offsetLeft

💙 offsetLeft = left + marginLeft

  1. 对块级元素来说,offsetTopoffsetLeftoffsetWidthoffsetHeight 描述了元素相对于 offsetParent 的边界框。

  2. 然而,对于可被截断到下一行的行内元素(如 span

    • offsetTopoffsetLeft 描述的是第一个边界框的位置(使用 Element.getClientRects() 来获取其宽度和高度),

    • offsetWidthoffsetHeight 描述的是边界框的尺寸(使用 Element.getBoundingClientRect 来获取其位置)。

    • 因此,使用 offsetLeft、offsetTop、offsetWidthoffsetHeight 来对应 left、top、width 和 height 的一个盒子将不会是文本容器 span 的盒子边界。

图解

<template>
  <div id="toTop" ref="toTop">
    <div class="body" ref="body">
      <p>body</p>
      <div class="h1">h1</div>
      <div class="outer">
        <span class="span">Short span.Long span that wraps withing this div.</span>
      </div>
      <div class="to" ref="to" @click="toTop">
        <p class="to_p">To</p>
      </div>
    </div>
  </div>
</template>

<style lang="scss">
#toTop {
  .body {
    position: relative;
    width: 95%;
    height: 1600px;
    background-color: #ecf0f1;
    margin: 50px 2.5%;
    border-radius: 5px;

    .h1 {
      display: inline-block;
      margin: 100px;
      width: 100px;
      border: 2px solid black;
    }

    .outer {
      width: 400px;
      .span {
        // line-height: 100px;
        border: 2px solid black;
      }
    }
    .to {
      position: fixed;
      bottom: 30px;
      right: 30px;
      height: 45px;
      width: 45px;
      border-radius: 50%;
      background-color: #3498db;

      &:hover {
        box-shadow: 0px 0px 3px 3px gainsboro;
      }

      p {
        // text-align: center;
        line-height: 45px;
        color: #fff;
        font-weight: bold;
      }
    }
  }
}
</style>

var body = document.querySelector(".body");

console.log("body Parent", body.offsetParent);
console.log("body Width", body.offsetWidth);
console.log("body Height", body.offsetHeight);
console.log("body Top", body.offsetTop);
console.log("body Left", body.offsetLeft);


console.log("h1 Parent", h1.offsetParent);
console.log("h1 Width", h1.offsetWidth);
console.log("h1 Height", h1.offsetHeight);
console.log("h1 Top", h1.offsetTop);
console.log("h1 Left", h1.offsetLeft);


 .outer {
      width: 400px;
      .span {
        border: 2px solid black;
      }
    }

console.log("span Parent", span.offsetParent);
console.log("span Width", span.offsetWidth);
console.log("span Height", span.offsetHeight);
console.log("span Top", span.offsetTop);
console.log("span Left", span.offsetLeft);


 .outer {
      width: 200px;
      .span {
        border: 2px solid black;
      }
    }

console.log("span Parent", span.offsetParent);
console.log("span Width", span.offsetWidth);
console.log("span Height", span.offsetHeight);
console.log("span Top", span.offsetTop);
console.log("span Left", span.offsetLeft);



console.log(to);

//在 Webkit内核浏览器 中 to.offsetParent 为 null
console.log("to Parent", to.offsetParent);
console.log("to Width", to.offsetWidth);
console.log("to Height", to.offsetHeight);
console.log("to Top", to.offsetTop);
console.log("to Left", to.offsetLeft);


console.log(to_p);

console.log("to_p Parent", to_p.offsetParent);
console.log("to_p Width", to_p.offsetWidth);
console.log("to_p Height", to_p.offsetHeight);
console.log("to_p Top", to_p.offsetTop);
console.log("to_p Left", to_p.offsetLeft);


设置了 line-height:45px 后
console.log(to_p);

console.log("to_p Parent", to_p.offsetParent);
console.log("to_p Width", to_p.offsetWidth);
console.log("to_p Height", to_p.offsetHeight);
console.log("to_p Top", to_p.offsetTop);
console.log("to_p Left", to_p.offsetLeft);

Scroll

scrollWidth

  1. Element.scrollWidth 这个只读属性是元素内容宽度的一种度量,包括由于overflow溢出而在屏幕上不可见的内容。

  2. scrollWidth 值等于元素在不使用水平滚动条的情况下适合视口中的所有内容所需的最小宽度

  3. 宽度的测量方式与 clientWidth 相同:它包含元素的内边距,但不包括边框,外边距或垂直滚动条(如果存在)。 它还可以包括伪元素的宽度,例如::before::after

  4. 如果元素的内容可以适合而不需要水平滚动条,则其scrollWidth等于 clientWidth

scrollHeight

  1. Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。

  2. scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度

  3. 没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值clientHeight相同。包括元素的padding,但不包括元素的border和margin。scrollHeight也包括 ::before::after这样的伪元素。

scrollTop

  1. Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。

  2. 一个元素的 scrollTop 值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。

  3. 当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为 0。

scrollLeft

  1. Element.scrollLeft 属性可以读取或设置元素滚动条到元素左边的距离。

  2. 注意如果这个元素的内容排列方向(direction) 是 RtoL (right-to-left) ,那么滚动条会位于最右侧(内容开始处),并且 scrollLeft 值为 0。此时,当你从右到左拖动滚动条时,scrollLeft会从0变为负数。

图解

<template>
  <div id="scroll">
    <button @click="get">btn</button>
    <div class="div1">
      <h1>1scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。 没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
    </div>
    <div @click="get" class="div2">div2</div>
  </div>
</template>

<script>
export default {
  name: "scroll",
  methods: {
    get() {
      var div1 = document.querySelector(".div1");
      var div2 = document.querySelector(".div2");

      div1.scrollLeft += 20;
      div1.scrollTop += 20;
      console.log("div1 height", div1.scrollHeight);
      console.log("div1 width", div1.scrollWidth);
      console.log("div1 top", div1.scrollTop);
      console.log("div1 left", div1.scrollLeft);

      div2.scrollLeft += 100;
      console.log("div2 height", div2.scrollHeight);
      console.log("div2 width", div2.scrollWidth);
      console.log("div2 top", div2.scrollTop);
      console.log("div2 left", div2.scrollLeft);
    }
  }
};
</script>
#scroll {
    position: relative;
    // overflow: scroll;
    height: 1000px;
    .div1 {
        overflow: auto;
        overflow-x: scroll;
        height: 300px;
        width: 300px;
        background-color: pink;
        
        h1:nth-child(1) {
            width: 400px;
        }
    }

    .div2 {
        position: absolute;
        height: 100px;
        width: 100px;
        background-color: gray;
        top: 200px;
        left: 500px;
    }
}

client

clientWidth

💚 width + padding

  1. 内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。

  2. Element.clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。

  3. 当在根元素( html 元素)上使用clientWidth时(或者在 body 上,如果文档是在quirks(怪异)模式下),将返回viewport的宽度(不包括任何滚动条).

clientHeight

💚 height+ padding

  1. 这个属性是只读属性,对于没有定义CSS或者内联布局盒子的元素为 0,否则,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。

  2. clientHeight 可以通过 CSS height + CSS padding - 水平滚动条高度 (如果存在)来计算.

clientTop

💚 border.Top

  1. 一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距clientTop 是只读的。

clientLeft

💚 border.Left

  1. 表示一个元素的左边框的宽度,以像素表示。clientLeft 是只读的。

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

图解

<template>
  <div id="client">
    <div class="div1">
      <h1>1scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。 没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
      <h1>1</h1>
    </div>
    <div class="div2" @click="get"></div>
  </div>
</template>

<script>
export default {
  name: "client",
  methods: {
    get() {
      var div1 = document.querySelector(".div1");

      console.log("设置宽高为300,client不包含滚动条");
      console.log("div1 H", div1.clientHeight);
      console.log("div1 W", div1.clientWidth);
      console.log("div1 T", div1.clientTop);
      console.log("div1 L", div1.clientLeft);
      console.log("div1 SH", div1.scrollHeight);
      console.log("div1 ST", div1.scrollTop);
      if (div1.scrollHeight - div1.scrollTop === div1.clientHeight) {
        console.log("到底了 div1 SH - ST", div1.scrollHeight - div1.scrollTop);
      }
    }
  }
};
</script>
#client {
    display: flex;
  .div1 {
    overflow: auto;
    overflow-x: scroll;
    height: 300px;
    width: 300px;
    background-color: pink;

    h1:nth-child(1) {
      width: 400px;
    }
  }

  .div2 {
    width: 100px;
    height: 100px;
    background-color: pink;
  }
}

//设置了边框后,火狐会有1px误差导致不能到底,而ie11 谷歌都正常 
.div1 {
    overflow: auto;
    overflow-x: scroll;
    height: 300px;
    width: 300px;
    background-color: pink;
  + border: 3px solid blue;

    h1:nth-child(1) {
      width: 400px;
    }
  }
//635 - 357 = 278


浏览器滚动条Top

console.log(window.pageYOffset); //火狐:浮点数,谷歌、ie11:
整数console.log(window.scrollY);  //火狐:浮点数,谷歌:整数,ie11:undefined
console.log(document.body.scrollTop); //均为 0
console.log(document.documentElement.scrollTop);  //均整数

测试浏览器滚动条是否到底

//滚动条顶部距离 + 浏览器内容高度 === 浏览器滚动条高度
if (document.documentElement.scrollTop + document.documentElement.clientHeight ===
	document.body.scrollHeight) 
{
	alert("到底了");
}


平滑滚动回顶部

this.timeid = setInterval(() => {
        document.documentElement.scrollTop -= 20;
        if (document.documentElement.scrollTop == 0) {
          window.clearInterval(this.timeid);
        }
      }, 20);