前端元素尺寸那些事

1,970 阅读7分钟

关于尺寸的属性

  • clientHeight和clientWidth:

    • 元素可视内容区域的高度和宽度,不包括水平滚动条、边框和外边距。比如,clientWidth=width+padding

    • 单位是像素px。整数(亲测,这是个四舍五入后的值)。返回值不带px

    • 只读属性

      image_1d03q576g1konsnt5pn8q53099.png-8.5kB

  • offsetHeight和offsetWidth:

    • 元素可视内容区域的高度和宽度,包括元素的边框、内边距和元素的水平滚动条(如果存在且渲染的话),不包括外边距和:before或:after等伪类元素的高度。比如,offsetWidth=width+padding+borderWidth

    • 单位是像素px。整数(亲测,这是个四舍五入后的值)。返回值不带px

    • 只读属性

      image_1d03q576g1konsnt5pn8q53099.png-8.5kB

  • scrollHeight 和 scrollWidth:

    • 元素内容的实际高度和宽度,包括由于溢出导致的视图中不可见的内容,也包括:before或:after等伪类元素的高度和宽度,不包括边框和外边距。没有垂直滚动条的情况下,scrollHeight与clientHeight相等。
    • 单位是像素px。整数(亲测,这是个四舍五入后的值)。返回值不带px
    • 只读属性

image_1d03q576g1konsnt5pn8q53099.png-8.5kB

应用

一 获取视口和页面的大小

网页大小:一张网页的全部面积,包括不可见的滚动内容,这个大小由代码设置。

浏览器大小:浏览器窗口看到的面积,也叫视口。

// 视口大小
function getViewport() {
  if (document.compatMode == 'BackCompat') {    // 文档模式判断
    return {
      width: document.body.clientWidth,
      height: document.body.clientHeight
    };
  } else {
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    };
  }
}

// 网页大小
function getPagearea() {
  if (document.compatMode == 'BackCompat') {
    return {
      width: document.body.scrollWidth,
      height: document.body.scrollHeight
    };
  } else {
    return {
      width: document.documentElement.scrollWidth,
      height: document.documentElement.scrollHeight
    };
  }
}

二 判断元素是否滚动到底

如果元素已经滚动到底,则它的实际内容高度减去已滚动的距离应该等于可视区域高度。下面等式返回true代表已经到底,反之则没有。

element.scrollHeight - element.scrollTop === element.clientHeight

关于元素的位置

关于位置的属性

  • offsetTop和offsetLeft:

    • 元素距离offsetParent的高度。offsetParent指的是距离该元素最近的position不为static的祖先元素。如果没有则指向body元素。

    • 单位是像素px。整数(亲测,这是个四舍五入后的值)。返回值不带px

    • 只读属性

      image_1d03q6gme1li1igs1i2dllg15lqm.png-10.9kB

  • scrollTop 和 scrollLeft:

    • 元素的滚动距离。比如,scrollTop代表滚动条向下滚动的距离,也是是元素顶部被遮住的距离。在没有滚动条的时候,scrollTop等于0。

    • 单位是像素px。整数(亲测,这是个四舍五入后的值)。返回值不带px

    • 可写属性:如果设置小于0,它的值会等于0。如果设置超过了这个容器可滚动的距离,它的值会等于最大值

      image_1d03q6gme1li1igs1i2dllg15lqm.png-10.9kB

应用

一 获取元素绝对位置

绝对位置,指元素的左上角相对于整张网页左上角的坐标,通过计算获得。每个元素都有 offsetTop 和 offsetLeft 属性,表示该元素的左上角与父容器(offsetParent对象)左上角的距离,因此进行迭代累加,就可以得到该元素的绝对坐标。注意,此方面不适用于 表格 和 iframe,因为在其中,offsetParent 对象未必等于父容器。

function getElementTop(element) {
  let actualTop = element.offsetTop;
  let current = element.offsetParent;
  while (current !== null) {
	actualTop += current.offsetTop;
	current = current.offsetParent;
  }
  return actualTop;
}

注意: 所有这些偏移量属性都是只读的,而且每次访问它们都需要重新计算。因此,应该尽量避免重复访问这些属性;如果需要重复使用其中某些属性的值,可以将它们保存在局部变量中,以提高性能。

样式相关

getBoundingClientRect

使用方法:

rectObject = element.getBoundingClientRect();
  • 返回元素的大小及其相对于视口的位置,返回值是一个对象,包含width, height, bottom, right, left, top, 六个属性。left, right, top, bottom都是元素(不包括margin)相对于视口的原点(视口的上边界和左边界)的距离。

  • 单位是像素px。浮点值。返回值不带px

  • 只读属性,返回浮点值。

如下图,浏览器的支持程度还是非常高的。

自己测试的结果是,这个方法获取的height和offsetHeight大小是一致的。

element.style

使用方法:

inlineStyle = element.style;
  • 返回值是一个CSSStyleDeclaration对象,返回元素标签内的样式,局限性在于使用这个方法只能获取到元素内联样式中的属性值。其中,CSSStyleDeclaration 表示一个CSS属性键值对的集合
  • 单位是像素px。字符串。返回值带px
  • 可写属性

可以这样手动操作单个元素的宽度:

Element.style.width = '100px';

getComputedStyle

使用方法:

window.getComputedStyle(element, [pseudoElt]);

第二个参数是指定一个要匹配的伪元素的字符串,例如“:before”。如果不需要可以为null。CSS属性值可以使用getPropertyValue(propName)API或直接索引到对象。差别在于getPropertyValue不必使用驼峰的写法。

例如

window.getComputedStyle(ele, null).getPropertyValue("background-color");
window.getComputedStyle(ele, null).backgroundColor;
  • 返回值是一个CSSStyleDeclaration对象,返回一个可以获取当前元素所有最终使用的CSS属性值
  • 单位是像素px。字符串。返回值带px
  • 只读属性

如下图,浏览器的支持程度还是非常高的。

element.currentStyle

这个属性是IE浏览器的属性。语法与element.style类似,差别在于element.currentStyle返回的是元素当前应用的最终CSS属性值(包括外链CSS文件,页面中嵌入的<style>属性等)。

应用

一 判断元素是否在可视区域

// 方法1
function isInViewPortOfTwo (el) {
  const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
  const top = el.getBoundingClientRect() && el.getBoundingClientRect().top
  const offsetTop = el.offsetTop
  const scrollTop = document.documentElement.scrollTop
  const topGap = offsetTop - scrollTop
  return topGap  <= viewPortHeight
}
// 方法2
function isInViewPortOfTwo (el) {
  const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  const { top, height } = el.getBoundingClientRect();
  return (top <= viewPortHeight) && (top + height >= 0);
}

二 图片懒加载

实现一个非常简易的图片懒加载

<img 
  class="test-img"
  src="" 
  data-src="httt://www.xxx.com/images/001.jpg"
/>

监听scroll事件,当图片出现在可视区域时,提取data-src的值并赋给src,加载真正图片

let img = document.getElementsByClassName('test-img');

//设置每次遍历的起始图片,防止重复加载
let n = 0
//首次加载可视区域图片
lazyLoad();

function lazyLoad() {
  for (let i = n; i<img.length;i++) {
    let el = img[i];
    if(el.getAttribute('src') === '' && isInViewPortOfTwo(el)){
      el.setAttribute('src', el.getAttribute('data-src'))
      n++;
    }
  }
}
// 判断是否在可视范围内
function isInViewPortOfTwo (el) {
  const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  const top = el.getBoundingClientRect() && el.getBoundingClientRect().top;
  return top  <= viewPortHeight
}

window.addEventListener('scroll', _.debounce(lazyLoad, 1000))

三 获取元素的位置

// 相对位置
var X= this.getBoundingClientRect().left;
var Y =this.getBoundingClientRect().top;

// 绝对位置
var X= this.getBoundingClientRect().left + document.documentElement.scrollLeft;
var Y =this.getBoundingClientRect().top  +document.documentElement.scrollTop;

测试代码附上

<!DOCTYPE html>
<html>
<head>
  <style>
    * {
      padding: 0;
      margin: 0;
    }
    #app {
      border: 1px solid #333333;
      margin-top: 50px;
      padding-top: 50px;
    }
    #box1 {
      width: 100px;
      height: 50px;
      padding: 50px;
      border: 50px solid #000000;
      background-color: #00D8C9;
      font-size: 20px;
      overflow: scroll;
    }
    #box2 {
      width: 200px;
      height: 200px;
      margin-top: 500px;
      border: 50px solid #FF4747;
      background-color: #00D8C9;
    }
  </style>
</head>
<body>
  <div id="app">
    <div id="box1">第一个盒子第一个盒子第一个盒子第一个盒子第一个盒子第一个盒子</div>
    <div id="box2" style="width: 500px; height: 100px">第一个盒子</div>
  </div>
<script>
let box1 = document.getElementById('box1');
let box2 = document.getElementById('box2');
function getViewport() {
  // 文档模式判断
  if (document.compatMode == 'BackCompat') {
    return {
      width: document.body.clientWidth,
      height: document.body.clientHeight
    };
  } else {
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    };
  }
}

function getPagearea() {
  if (document.compatMode == 'BackCompat') {
    return {
      width: document.body.scrollWidth,
      height: document.body.scrollHeight
    };
  } else {
    return {
      width: document.documentElement.scrollWidth,
      height: document.documentElement.scrollHeight
    };
  }
}

console.log('视口宽度', getViewport().width)
console.log('视口高度', getViewport().height)
console.log('网页宽度', getPagearea().width)
console.log('网页高度', getPagearea().height)

console.log('---------元素尺寸----------')
console.log('第一个元素的高度,clientHeight', box1.clientHeight)
console.log('第一个元素的高度,offsetHeight', box1.offsetHeight)
console.log('第一个元素的高度,scrollHeight', box1.scrollHeight)
console.log('第一个元素的高度,scrollTop', box1.scrollTop)

console.log('---------元素距离----------')
console.log('第二个元素距离父元素的高度 offsetTop', box2.offsetTop)

console.log('---------元素样式----------')
console.log('第二个元素的DOMRect getBoundingClientRect', box2.getBoundingClientRect())

console.log('第二个元素的 style', box2.style)
console.log('第二个元素的 getComputedStyle', window.getComputedStyle(box2).getPropertyValue('width'))
</script>
</body>
</html>

博客都会先发布在github上。 如果有错误或者不严谨的地方,欢迎给予指正,十分感谢。 如果喜欢或者有所帮助,欢迎star。新手需要鼓励,希望大家不要吝啬自己的赞哦,赠人玫瑰,手有余香。

传送门github