图片懒加载的三种解决方法

简介: 学习前端,三板斧,HTML、CSS、JavaScript。JavaScript 基础由 DOM、BOM、ECMAScript 组成,其中 ECMAScript 为规范语言,现在说的 ES6(ES2016~ES2022) 指的就是它,隔段时间就会发布,目前一年发布一次,以年份来说,现在是 ECMAScript 2022。

学习前端,三板斧,HTML、CSS、JavaScript。JavaScript 基础由 DOM、BOM、ECMAScript 组成,其中 ECMAScript 为规范语言,现在说的 ES6(ES2016~ES2022) 指的就是它,隔段时间就会发布,目前一年发布一次,以年份来说,现在是 ECMAScript 2022。BOM是什么,BOM是浏览器对象模型(Browser Object Model)。它有六大对象


  • document:DOM(对 BOM 包含了DOM,但是 DOM 重要,其地位和 BOM 一样)


  • event:事件对象


  • history:浏览器的历史记录


  • location:窗口的 url 地址栏信息


  • screen:显示设备的信息


  • navigator:浏览器的配置信息


DOM我们也很了解,文本对象模型,指操作HTML(超级文本标识语言)的API。DOM 会将文档解析为一个由节点和对象(包含属性和方法的对象)组件的结构集合


以前开发页面时,我们在 script 标签中,先获取节点(DOM Api),再操作 DOM ,所以以前是 《JavaScript 面向对象编程》,《JavaScript dom编程艺术》,但操作 DOM 的 API 太长,不易书写,JQuery 集大成,简化Api,统一了操作写法。如果展开,会有很多可以延伸,而我铺垫了这么多,就是想引出 document


这次我们要讲的 offset、scroll、client 就是出自“ document 家”。先配两张图来看看这三个到底是什么


image.png


image.png


client



client 指元素本身的可视内容。不包括 overflow 被折叠部分,不包括滚动条、border,包括 padding


有四属性:


  • clientHeight:对象可见的高度


  • clientWidth:对象可见宽度


  • clientTop:元素距离顶部的厚度,一般为0,因为滚动条不会出现在顶部


  • clientLeft:元素距离左侧的厚度,一般为0,因为滚动条不会出现在左侧


offset



offset 指偏移。包括这个元素在文档中占用的所有显示宽度,包括滚动条、padding、border,不包括 overflow 隐藏的部分


有五属性:


offsetHeight:该对象自身的绝对高度,


  • offsetHeight: = border-width * 2 + padding-top + height + padding-bottom


offsetWidth:该对象自身的绝对宽度


  • offsetWidth = border-width * 2 + padding-left + width + padding-right


offsetParent:返回一个对象的引用,字面意思,相对父元素的偏移


  • 如果当前元素的父元素没有 CSS 定位(position为absolute/relative),offsetParent 为 body


  • 如果当前元素的父元素有 CSS 定位(position为absolute/relative),offsetParent 取父级中最近的元素


offsetTop:相对版面或 offsetParent 属性指定父坐标的顶部距离


  • offsetTop = offsetParent 的 padding-top + 中间元素的 offsetHeight + 当前元素的 margin-top


offsetLeft:相对版面或 offsetParent 属性指定父坐标的左部距离


  • offsetLeft = offsetParent 的 padding-left + 中间元素的 offsetWidth + 当前元素的 margin-left


Scroll


Scroll 指滚动。包括这个元素没有显示出来的实际宽度,包括 padding,不包括滚动条、border


scrollHeight:获取对象的滚动高度,对象的实际高度


scrollWidth:获取对象的滚动宽度


scrollTop:当前元素与窗口最顶端的距离


scrollLeft:当前元素与窗口最左端的距离


其他



innerHeight 和 clientHeight 有什么区别


准确来说,clientHeight 是针对 body,innerHeight 是 window 的


document.body.clientHeight:网页可见区域高


window.innerHeight:可视窗口高度,不包括浏览器顶部工具栏


监听图片高度实现懒加载



通过图片的 offsetTop(偏移高度)和 window 的 innerHeight、scrollTop 判断图片是否位于可视区域


即很多图片,先显示视窗中的图片,没看见的先不展示,加快页面加载速度。当你向下滚,当后续图片的 offsetTop(偏移高度) 小于 innerHeight(视窗高度) + scrollTop(滚动高度) 时,意味着此图片已经出现在视窗中,将真正图片替换loading


关键代码在于


function lazyload() {
   let seeHeight = window.innerHeight;
   let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
   for (let i = n; i < img.length; i++) {
       if (img[i].offsetTop < seeHeight + scrollTop) { // 对比图片的偏移高度和屏幕高度+滚动高度
           if (img[i].getAttribute("src") === 'loading.gif') {
               img[i].src = img[i].getAttribute("data-src")
          }
           n = i + 1
      }
  }
}


效果如下:


image.png


Element.getBoundingClientRect



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


getBoundingClientRect 返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合,即与该元素相关的 CSS 边框集合


语法


domRect = element.getBoundingClientRect();


返回坐标、宽高、在视口中的位置


  • x
  • y
  • width
  • height
  • top
  • right
  • bottom
  • left


image.png


如果是标准盒子模型,元素的尺寸等于 width/height + padding + border-width 的总和。如果 box-sizing:border-box ,元素的尺寸等于 width/height


我们用这个 API 来获取每张图片的 top 值,如果 top 值小于可视区的高度就视为已经进入可视区,直接加载图片即可


function lazyload() {
   let seeHeight = document.documentElement.clientHeight
   for (let i = n; i < img.length; i++) {
       if (img[i].getBoundingClientRect().top < seeHeight) {
           if (img[i].getAttribute("src") === "loading.gif") {
               img[i].src= img[i].getAttribute("data-src")
          }
           n = i + 1
      }
  }
}


效果如下:

image.gifimage.png


通过 IntersectionObserver 实现懒加载



IntersectionObserver 接口(从属于 Intersection Observer API)提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)


IntersectionObserver 可以不用监听 scroll 事件,做到元素一可见便调用回调,在回调里面我们来判断元素是否可见


if (IntersectionObserver) {
   let lazyImageObserver = new IntersectionObserver((entries, observer) => {
       entries.forEach((entry, index) => {
           let lazyImage = entry.target;
           // 如果元素可见
           if (entry.intersectionRatio > 0) {
               if (lazyImage.getAttribute("src") === 'loading.gif') {
                   lazyImage.src = lazyImage.getAttribute("data-src")
              }
               lazyImageObserver.unobserve(lazyImage)
          }
      })
  })
   for (let i = 0; i < img.length; i++) {
       lazyImageObserver.observe(img[i])
  }
}


上述代码表示,遍历所有的图片,对其进行观察原生是否可见,如果元素可见,就把真正图片替换loading


IntersectionObserver  可以自动“观察”元素是否可见,其本质是目标元素与视窗产生一个交叉去,所以这个 API 叫做“交叉观察器”


使用方式


let io = new IntersectionObserver(callback, option)


上面代码中, IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:callback 是可见性变化时的回调函数,option 是配置对象(该参数可选)


构造函数的返回值是一个观察器实例。实例的 observe 方法可以指定观察哪个 DOM 节点


// 开始观察
io.observe(document.getElementById("example"))
// 停止观察
io.unobserve(element)
// 关闭观察器
io.disconnect()


callback 参数


目标元素的可见性变化时,就会调用观察器的回调函数 callback


callback 一般会触发两次。一次时目标元素刚刚进入视窗(开始可见),另一次时完全离开视窗(开始不可见)


let io = new IntersectionObserver(
   entries => {
       console.log(entries)
  }
)


上面代码中,回调函数采用的是箭头函数的写法。callback 函数的参数(entries)是一个数组,每个成员都是一个 IntersectionObserverEntry 对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。


IntersectionObserverEntry 对象


IntersectionObserverEntry 对象提供目标元素的信息,一共有六个属性


  • time: 可见性发生变化的时间,单位毫秒


  • target:被观察的目标,是个 DOM 节点对象


  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect() 方法的返回值,如果没有根元素(即直接相对于视窗滚动),则返回null


  • boundingClientRect:目标元素的矩形区域的信息


  • intersectionRect:目标元素与视窗(或根元素)的交叉区域的信息


  • intersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为1,完全不可见时小于等于 0  


如图所示:


image.png


兼容性如何


caniuse 兼容性报告目前支持率是 93.67%,但是iOS的支持度要在 iOS12.2 以上,如果是iPhoneX(2018.11)之后的手机都是支持的,如果是之前的,升级系统才支持,考虑到一些人是不会升级,所以这个兼容性还不支持大众化的场景,但它的能力和性能都非常的好


image.png


总结



面试的时候被问到懒加载,我那个时候没做过相关的准备,我说不知道,X虎的面试官会引导,其实引导才能测试出一个人真正的水平,但是那个时候我竟然连 scroll 都想不起来。现在回想起来,实在是准备的方向搞错了。


说到图片懒加载,有三种方法:


  • 监听图片高度


  • 技术要点:监听scroll,滚动的时候遍历所有的图片,如果图片的偏移高度小于屏幕高度+滑动高度,说明已经出现在视窗,就替换图片
  • 优点:兼容性好
  • 缺点:单纯使用 scroll 滑动来监听高度,会引发性能问题,所以要搭配节流


  • Element.getBoundingClientRect


  • 技术要点:与监听图片无太大区别,无非是把图片的偏移高度改成 getBoundingClientRect().top,对比每张图片的自身高度是否出现在视窗(视口)中,有就替换图片
  • 优点:兼容性好,代码相对监听图片高度少了一些
  • 缺点:也是使用 scroll 滑动来监听,会引发性能问题


  • 使用 IntersectionObserver Api


  • 技术要点:通过 IntersectionObserver Api 来实现,图片元素一可见就调用回调,在回调中判断元素是否可见
  • 优点:写起来方便,性能好
  • 缺点:兼容性适配iOS12.2以上,安卓5以上


附上线上 demo:


  • scroll


  • getBoundingClientRect


  • IntersectionObserver


参考资料



  • JavaScript学习总结(三)BOM和DOM详解


  • JS 中的offset、scroll、client总结


  • IntersectionObserver API 使用教程



相关文章
|
物联网 网络性能优化 API
MQTT常见问题之单个消息发送数据不能超过64k如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
存储 Java
【JVM】 程序计数器(Program Counter Register)
【JVM】 程序计数器(Program Counter Register)
504 1
|
数据可视化 数据挖掘 数据管理
问卷调查数据分析指南!掌握方法,精准把握用户需求!
本文介绍了如何利用自定义报表、交叉报表和过滤器进行问卷调查数据分析。文章首先区分了定量和定性数据,强调了定量数据在分析中的重要性,并列举了客户体验(CSAT、CES、NPS)和市场调研的关键指标。接着,提到了定性数据分析方法,如情感分析和词云图。文章还讨论了自定义报表、交叉报表和过滤器在数据筛选和相关性探索中的作用,以及收集器在多源数据收集上的应用。最后,强调了仪表板在数据可视化和比较中的优势,并推荐了Zoho Survey作为综合的数据管理平台。
758 0
问卷调查数据分析指南!掌握方法,精准把握用户需求!
|
弹性计算 负载均衡 Kubernetes
slb的LoadBalancer
slb的LoadBalancer
362 2
都8102年了,还用fastq-dump,快换fasterq-dump吧
之前写过一篇文章Fastq-dump: 一个神奇的软件, 详细介绍了fastq-dump的用法。 虽然fastq-dump参数很多,而且一直被吐槽参数说明写的太差,但是如果真的要用起来其实也就是一行代码 fastq-dump --gzip --split-3 --defline-qual &#39;+&#39; --defline-seq &#39;@$ac-$si/$ri&#39; SRRXXXXX| SRRXXXX.sra # 加上--gzip后需要时间进行文件压缩 当然除了参数问题,还有一个让人诟病的地方就是他只能单个线程,所以速度特别的慢。
5425 0
都8102年了,还用fastq-dump,快换fasterq-dump吧
|
数据采集 存储 JavaScript
构建您的第一个Python网络爬虫:抓取、解析与存储数据
【9月更文挑战第24天】在数字时代,数据是新的金矿。本文将引导您使用Python编写一个简单的网络爬虫,从互联网上自动抓取信息。我们将介绍如何使用requests库获取网页内容,BeautifulSoup进行HTML解析,以及如何将数据存储到文件或数据库中。无论您是数据分析师、研究人员还是对编程感兴趣的新手,这篇文章都将为您提供一个实用的入门指南。拿起键盘,让我们开始挖掘互联网的宝藏吧!
|
机器学习/深度学习 编解码 大数据
Google Earth Engine(GEE)——LandScan人口数据集
Google Earth Engine(GEE)——LandScan人口数据集
466 0
|
数据挖掘
2022亚太数学杯数学建模竞赛C题(思路、程序......)
2022亚太数学杯数学建模竞赛C题(思路、程序......)
597 0
|
JSON 前端开发 测试技术
Javaweb之SpringBootWeb案例员工管理之新增员工的详细解析
Javaweb之SpringBootWeb案例员工管理之新增员工的详细解析
217 0
|
存储 Kubernetes 前端开发
基于Kubernetes的实战案例分享
基于Kubernetes的实战案例分享