原生js实现图片懒加载

532 阅读1分钟

1 几个和图片懒加载相关的API

clientHeight,clientWidth
element.clientHeight
element.clientWidth

MDN上的解释

clientHeight`可以计算为:CSS `height`+ CSS `padding`- 水平滚动条的高度(如果存在)
clientWidth`可以计算为:CSS `width`+ CSS `padding`- 垂直滚动条的高度(如果存在)

直观的图解

dimensions-client.png

offSetTop
element.offsetTop

MDN上的解释

它返回当前元素相对于其offsetParent元素的顶部内边距的距离
getBoundingClientRect
Element.getBoundingClientRect()

MDN上的解释

Element.getBoundingClientRect() 方法返回元素的大小和相对于视口的位置。

如果是标准盒子模型,元素的尺寸等于`width/height``padding`+`border-width`的总和。如果`box-sizing: border-box`,元素的的尺寸等于`width/height`如果:offsetTop-scroolTop<clientHeight,则图片进入了可视区内,则被请求

返回的结果是包含完整元素的最小矩形,并且拥有`left``top``right``bottom``x``y``width`,和 `height`这几个以像素为单位的只读属性用于描述整个左边缘

直观的图解

1111.png

IntersectionObserver
IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。祖先元素与视窗(viewport)被称为根(root)

具体使用方法参考我的文章 IntersectionObserver接口详解

2 图片懒加载思路

就一个思路,只有图片进入了可视区内才被请求
方法一

222.jpg

如果:offsetTop-scroolTop<clientHeight,则图片进入了可视区内,则被请求
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>图片懒加载</title>
  <style>
    img {
      width: 100%;
      height: 100%;
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
<div>
  <img data-src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg" alt="">
  <img data-src="https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg" alt="">
</div>
</body>
<script>
  const imgs = document.querySelectorAll('img')

  //如果当前元素offsetParent存在则向上循环直至offsetParent不存在
  function getTop(e) {
    let T = e.offsetTop
    while (e = e.offsetParent) {
      T += e.offsetTop
    }
    return T
  }

  function lazyLoad(imgs) {
    const H = document.documentElement.clientHeight//获取可视区域高度
    const S = document.documentElement.scrollTop || document.body.scrollTop
    for (let i = 0; i < imgs.length; i++) {
      if (H + S > getTop(imgs[i])) {
        imgs[i].src = imgs[i].getAttribute('data-src')
      }
    }
  }

  window.onload = window.onscroll = function () { //onscroll()//滚动时触发
    lazyLoad(imgs)
  }
</script>
</html>
方法二
Element.getBoundingClientRect().top<=clientHeight`时,图片是在可视区域内的

js代码如下

 const imgs = document.querySelectorAll('img');

        function isIn(el) {
            const top = el.getBoundingClientRect().top;
            const clientHeight = window.innerHeight;
            return top <= clientHeight;
        } 
        //检查图片是否在可视区内,如果不在,则加载
        function check() {
            Array.from(imgs).forEach(function(el){
                if(isIn(el)){
                    loadImg(el);
                }
            })
        }
        function loadImg(el) {
            if(!el.src){
                cosnt source = el.dataset.src;
                el.src = source;
            }
        }
        window.onload = window.onscroll = function () { //onscroll()滚动时触发
            check();
        }
方法三

使用IntersectionObserver接口

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>图片懒加载</title>
  <style>
    img {
      width: 100%;
      height: 250px;
    }
  </style>
</head>
<body>
<img data-src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg" alt="">
<img data-src="https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg" alt="">
</body>
<script>

  let imgList = document.querySelectorAll('img')

  let observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.src = entry.target.dataset.src
        observer.unobserve(entry.target)
      }
    })
  })
  imgList.forEach(img => {
    observer.observe(img)
  })
</script>
</html>