Live2d Test Env

JavaScript小面试~~JavaScript实现图片懒加载,多方式解决加载过多问题

图片懒加载,就是滚动页面时,图片未出现在可视局域时不加载图片,只有图片出现在可视区域才加载。

思路:通过上面一段话,实现图片懒加载需要知道:

  • 绑定滚动事件
  • 可视窗口高度(VH)
  • 图片元素距离可视局域顶部距离(EH)
  • 如何加载图片

当VH大于EH时,图片出现在可视窗口,反之。

如何加载图片呢?我们可以为img标签绑定任意非其属性名的属性,将图片路径作为其属性值,例如:

<img data-src="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">
<img imgUrl="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">
<img aabbc="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">

都是可以的,这里我们使用

<img data-src="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">

来举例子:

首先需要获取img元素的DOM对象集合

let imgs = document.querySelectorAll('img')

遍历img的DOM对象集合,获取每个img的DOM对象

imgs.forEach(item => {
   // 获取到每个img距离可视区域顶部的距离和可视局域的高度,并进行比较
   if (item.getBoundingClientRect().top < window.innerHeight) {
      //    获取元素属性为data-src的属性值,并赋值给imgDOM实例的src属性上
       item.setAttribute('src',item.getAttribute('data-src'))
        }
 })

通过DOM对象的getBoundingClientRect().top获取该元素距离视口顶部的高度,通过window.innerHeight获取到视口高度。

item.getAttribute('data-src'):表示获取该DOM对象上名data-src属性的属性值。

item.setAttribute(attributeName,attributeValue):表示将attributeValue值作为属性值赋值给attributeName属性。

此时我们需要绑定一个滚动事件,这个事件绑定给window,每次只要滚动条发生滚动都会触发:

  let imgs = document.querySelectorAll('img')
        // 为window绑定滚动事件
  window.addEventListener('scroll', (e) => {
     // 遍历img的DOM对象集合,获取每个img的DOM对象
     mgs.forEach(item => {
         // 获取到每个img距离可视区域顶部的距离和可视局域的高度,并进行比较
         if (item.getBoundingClientRect().top < window.innerHeight) {
         //获取元素属性为data-src的属性值,并赋值给imgDOM实例的src属性上
            item.setAttribute('src',item.getAttribute('data-src'))
         }
    })
})

这是会有一个问题,就是每次滚动都会触发滚动事件,这会导致资源加载过多,浪费资源。

这里给出的解决方法有三个,

2,考虑兼容性:使用防抖或者节流

这里使用节流来举例(防抖可以自己去实现)

<script>
    // 1获取img元素的DOM对象
    let imgs = document.querySelectorAll('img')
    // 为window绑定滚动事件
    window.addEventListener('scroll', imgLazyLoading())
    function imgLazyLoading() {
        let timer
        return () => {
            if (timer) {
                return;
            }
            timer = setTimeout(() => {
                imgs.forEach(item => {
                    // 获取到每个img距离可视区域顶部的距离和可视局域的高度,并进行比较
                    if (item.getBoundingClientRect().top < window.innerHeight) {
                        //    获取元素属性为data-src的属性值,并赋值给imgDOM实例的src属性上
                        item.setAttribute('src', item.getAttribute('data-src'))
                    }
                })
                timer = null
            }, 500)
        }
    }
</script>

节流或者防抖也会导致图片加载过多,但是比起不加,这种方式更加节省浏览器资源。

3,不考虑兼容性

使用IntersectionObserver构造函数(浏览器提供的构造函数,有些浏览器不支持)。这个是可以实现交叉观察,什么是交叉观察呢?就是可以知道目标元素和可视窗口是否发生交叉局域

IntersectionObserver构造函数提供了两个方法用于对目标元素进行观察和取消观察。

observe(DOM节点):观察元素

unobserve(DOM节点):取消观察

创建实例:

  let observer=new  IntersectionObserver(callBack)

callBack是一个回调函数,它执行两次,一次是目标元素和可视窗口发生交叉时调用,一次是不发生交叉时调用(看见时调用一次,看不见时调用一次)

callBack回调函数接收一个参数,该参数包含被观察DOM节点相关信息和观察状态的数组,其中我们需要的是每个数组项中的:

target:被观察的DOM节点实例

isIntersecting:是否被观察到,观察到的时候,我们只需要通过unobserve(DOM节点)取消观察。完成代码为:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    div {
        height: 100vh;
        width: 1000px;
        position: relative;
        top: 50%;
        left: 50%;
        transform: translate(50% 50%);
    }
    img {
        padding-top: 50px;
        margin-bottom: 50px;
        height: 500px;
        width: 500px;
        background-repeat: no-repeat;
        background-size: 100% 100%;
        display: inline-block;
        border-top: 2px solid red;
    }
</style>

<body>
    <div>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>11111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>11111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>11111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>11111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <img data-src="./img/7ff513a853b6d50887606dc436b1ddff.jpeg" alt="">
        <img data-src="./img/8a728780b5ddd5672eb5bf8a4ce23c86.jpeg" alt="">
        <img data-src="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">
        <img data-src="./img/7ff513a853b6d50887606dc436b1ddff.jpeg" alt="">
        <img data-src="./img/8a728780b5ddd5672eb5bf8a4ce23c86.jpeg" alt="">
        <img data-src="./img/ec6c60c8c5de6d7fa899807cfa8d46f1.jpeg" alt="">
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
        <p>3ewrw山东省地方第三方的范德萨发水电费第三方第三方电风扇方式对方水电费</p>
        <p>1111111111111111111111111111111111111111111111111111111111111111</p>
        <p>22222222222222222222222222222222222222222222222222222222222222</p>
    </div>
</body>
<script>
    let imgs = document.querySelectorAll('img')
    let  observer=new  IntersectionObserver(callBack)
    imgs.forEach(imgItem=>{
        // 观察节点,触发new  IntersectionObserver(callBack)内的回调函数
        observer.observe(imgItem)
    })
    // IntersectionObserver会向回调函数传入一个参数
    function callBack(observerArray){
        // observerArray:每个被观察元素对象的实例和观察状态集合
        observerArray.forEach(item=>{
             // item:被观察元素对象的实例和观察状态信息
            //  isIntersecting:是否被观察到
            if(item.isIntersecting){
                // item.target:被观察到实例
                let imgDom=item.target
                imgDom.setAttribute('src', imgDom.getAttribute('data-src'))
                // 被观察到取消观察
                observer.unobserve(imgDom)
            }
            console.log(item);
        })
    }
</script>
</html>

(LEARN FROM 技术蛋老师)

看都看到这了,点个赞吧,大晚上的写也不容易~~~~~算我球球你了(AQA)

posted @ 2021-09-07 00:30  waywardcode  阅读(175)  评论(0)    收藏  举报