原生 JS 实现最简单的图片懒加载
懒加载
什么是懒加载
懒加载其实就是延迟加载,是一种对网页性能优化的方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
什么时候用懒加载
当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
懒加载原理
我们都知道HTML中的 <img>标签是代表文档中的一个图像。。说了个废话。。
<img>标签有一个属性是 src,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有 src属性,就不会发送请求。
嗯?貌似这点可以利用一下?
我先不设置 src,需要的时候再设置?
nice,就是这样。
我们先不给 <img>设置 src,把图片真正的URL放在另一个属性 data-src中,在需要的时候也就是图片进入可视区域的之前,将URL取出放到 src中。
实现
HTML结构
<div class="container"><div class="img-area"><img class="my-photo" alt="loading" src="./img/img1.png"></div><div class="img-area"><img class="my-photo" alt="loading" src="./img/img2.png"></div><div class="img-area"><img class="my-photo" alt="loading" src="./img/img3.png"></div><div class="img-area"><img class="my-photo" alt="loading" src="./img/img4.png"></div><div class="img-area"><img class="my-photo" alt="loading" src="./img/img5.png"></div></div>
仔细观察一下, <img>标签此时是没有 src属性的,只有 alt和 data-src属性。
alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过
HTMLElement.dataset来访问。
如何判断元素是否在可视区域
方法一
网上看到好多这种方法,稍微记录一下。
通过
document.documentElement.clientHeight获取屏幕可视窗口高度通过
document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离通过
element.offsetTop获取元素相对于文档顶部的距离
然后判断②-③<①是否成立,如果成立,元素就在可视区域内。
方法二(推荐)
通过 getBoundingClientRect()方法来获取元素的大小以及位置,MDN上是这样描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为 ClientRect的 DOMRect对象,包含了 top、 right、 botton、 left、 width、 height这些值。
MDN上有这样一张图:

可以看出返回的元素位置是相对于左上角而言的,而不是边距。
我们思考一下,什么情况下图片进入可视区域。
假设 constbound=el.getBoundingClientRect();来表示图片到可视区域顶部距离; 并设 constclientHeight=window.innerHeight;来表示可视区域的高度。
随着滚动条的向下滚动, bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当 bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。
我们这样判断:
function isInSight(el) {const bound = el.getBoundingClientRect();const clientHeight = window.innerHeight;//如果只考虑向下滚动加载//const clientWidth = window.innerWeight;return bound.top <= clientHeight + 100;}
这里有个+100是为了提前加载。
加载图片
页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。
function checkImgs() {const imgs = document.querySelectorAll('.my-photo');Array.from(imgs).forEach(el => {if (isInSight(el)) {loadImg(el);}})}function loadImg(el) {if (!el.src) {const source = el.dataset.src;el.src = source;}}
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
函数节流
在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
获取第一次触发事件的时间戳
获取第二次触发事件的时间戳
时间差如果大于某个阈值就执行事件,然后重置第一个时间
function throttle(fn, mustRun = 500) {const timer = null;let previous = null;return function() {const now = new Date();const context = this;const args = arguments;if (!previous){previous = now;}const remaining = now - previous;if (mustRun && remaining >= mustRun) {fn.apply(context, args);previous = now;}}}
这里的 mustRun就是调用函数的时间间隔,无论多么频繁的调用 fn,只有 remaining>=mustRun时 fn才能被执行。
实验
页面打开时

可以看出此时仅仅是加载了img1和img2,其它的img都没发送请求,看看此时的浏览器

第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
页面滚动时
当我向下滚动,此时浏览器是这样

此时第二张图片完全显示了,而第三张图片显示了一点点,这时候我们看看请求情况

img3的请求发出来,而后面的请求还是没发出~
全部载入时
当滚动条滚到最底下时,全部请求都应该是发出的,如图

更新
方法三 IntersectionObserver
经大佬提醒,发现了这个方法
先附上链接:
jjc大大:
https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver可以自动观察元素是否在视口内。
var io = new IntersectionObserver(callback, option);// 开始观察io.observe(document.getElementById('example'));// 停止观察io.unobserve(element);// 关闭观察器io.disconnect();
callback的参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:
| 属性 | 描述 |
|---|---|
| time | 可见性发生变化的时间,单位为毫秒 |
| rootBounds | 与getBoundingClientRect()方法的返回值一样 |
| boundingClientRect | 目标元素的矩形区域的信息 |
| intersectionRect | 目标元素与视口(或根元素)的交叉区域的信息 |
| intersectionRatio | 目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0 |
| target | 被观察的目标元素,是一个 DOM 节点对象 |
我们需要用到 intersectionRatio来判断是否在可视区域内,当 intersectionRatio>0&&intersectionRatio<=1即在可视区域内。
代码
function checkImgs() {const imgs = Array.from(document.querySelectorAll(".my-photo"));imgs.forEach(item => io.observe(item));}function loadImg(el) {if (!el.src) {const source = el.dataset.src;el.src = source;}}const io = new IntersectionObserver(ioes => {ioes.forEach(ioe => {const el = ioe.target;const intersectionRatio = ioe.intersectionRatio;if (intersectionRatio > 0 && intersectionRatio <= 1) {loadImg(el);}el.onload = el.onerror = () => io.unobserve(el);});});
原生 JS 实现最简单的图片懒加载的更多相关文章
- 如何结合插件 vue-lazyload 来简单实现图片懒加载?
插件地址:https://www.npmjs.com/package/vue-lazyload: 一.使用场景: 在项目中有很多条数的信息,且图片很多的时候,不需要一次把整个页面的图片都加载完,而是在 ...
- jq demo 简单的图片懒加载效果
重点:在元素进入可视区域后,把图片元素的 _src 的值,赋值给 src <!DOCTYPE HTML> <html> <head> <meta http-e ...
- jQuery插件图片懒加载lazyload
来自XXX的前言: 什么是ImageLazyLoad技术 在页面上图片比较多的时候,打开一张页面必然引起与服务器大数据量的 交互.尤其是对于高清晰的图片,占的几M的空间.ImageLazyLoad技术 ...
- vue项目中实现图片懒加载的方法
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载.这样子对于页面加载性能上会有很大的提升,也提高了用户体验. 实 ...
- Vue项目中实现图片懒加载
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! ---对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面 ...
- 页面性能优化-原生JS实现图片懒加载
在项目开发中,我们往往会遇到一个页面需要加载很多图片的情况.我们可以一次性加载全部的图片,但是考虑到用户有可能只浏览部分图片.所以我们需要对图片加载进行优化,只加载浏览器窗口内的图片,当用户滚动时,再 ...
- 原生js开发,无依赖、轻量级的现代浏览器图片懒加载插件,适合在移动端开发使用
优势 1.原生js开发,不依赖任何框架或库 2.支持将各种宽高不一致的图片,自动剪切成默认图片的宽高 比如说你的默认图片是一张正方形的图片,则各种宽度高度不一样的图片,自动剪切成正方形. 完美解决移动 ...
- js原生图片懒加载 或 js原生图片预加载,html标签自定义属性
使用原声js来实现图片预加载,或图片懒加载,小伙伴们可以根据项目需要来结合vue或者是react来进行修改. 一.什么是图片懒加载或什么是图片预加载 当访问一个页面的时候,先把img元素或是其他元素的 ...
- 原生js实现图片懒加载+加入节流
这两天在学习图片的懒加载实现,看了很多大佬的博客,终于有了点成果.现在用了其中一位大佬的文章中的代码实现了图片懒加载并且在其基础上加入了节流函数. 原理就不多讲了,有需要的可以去大佬的文章看看.大佬文 ...
随机推荐
- HTML ------- 对文本进行操作的元素
1.HTML 标题(Heading) 在<h1> -- <h6> 标签进行定义,<h1>定义最大标题,<h6>定义最小的标题 作用:标题会自动加粗,大 ...
- 用vscode开发vue应用
阅读 3237 收藏 205 2019-05-02 原文链接:segmentfault.com 云服务器 1 核 2G , 9元/月 ,买十送二,99/年!!!快来上车!developer.huawe ...
- service的yaml说明
apiVersion: v1 kind: Service metadata: name: nginx-service labels: app: nginx spec: ports: - port: 8 ...
- k8s网络原理
https://blog.csdn.net/watermelonbig/article/details/80646988 k8s中,每个 Pod 都有一个独立的 IP 地址,所有 Pod 在一个网络空 ...
- 使用Docker之镜像的拉取、查询、删除
1:查看镜像列表 2:拉取镜像 通过命令可以从镜像仓库中拉取镜像,默认从Docker Hub 获取. 命令格式: docker image pull <repository>:< ...
- .NetCore使用NLog写入数据库总结
考虑到项目后期添加日志的需求,抽个闲暇时间学习一下使用NLog插件将日志信息写入到数据库中,完整项目见下面: 遇到的问题: 使用NLog写到SQLServer里面的中文显示问号? 解决方法:调整数据库 ...
- sql脚本来获取数据库中的所有表结构了
sql脚本来获取数据库中的所有表结构了,代码如下: use AdventureWorks2008 go SELECT (case when a.colorder=1 then d.name else ...
- Vue.js@2.6.10更新内置错误处机制,Fundebug同步支持相应错误监控
摘要: Fundebug 的 JavaScript 错误监控插件同步支持 Vue.js 异步错误监控. Vue.js 从诞生至今已经 5 年,尤大在今年 2 月份发布了重大更新,即Vue 2.6.更新 ...
- Spring之IoC详解(非原创)
文章大纲 一.Spring介绍二.Spring的IoC实战三.IoC常见注解总结四.项目源码及参考资料下载五.参考文章 一.Spring介绍 1. 什么是Spring Spring是分层的Java ...
- PHP如何创建文件夹(mkdir的用法,mkdirs的语法)【转】
(PHPmkdir如何创建多级目录) 在开始之前,我先说明一下,可能许多朋友与我一样认为只要给一个路径,mkdir就可以创建文件夹,其它不是那样,单个的MKDIR只能创建一级目录,对于多级的就不 ...