原生 JS 实现图片懒加载

1、什么是懒加载?

懒加载就是延迟加载的意思,比如我们加载一个页面,这个页面很长很长,长到我们的浏览器可视区域装不下,那么懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。

gif.gif

2、为什么要懒加载?

懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。

总结两个点:

  1. 全部加载的话会影响用户体验
  2. 浪费用户的流量,有些用户并不像全部看完,全部加载会耗费大量流量。

3、懒加载实现原理?

原理:大家都知道,一张图片就是一个 <img> 标签,而图片的来源主要是 src 属性。浏览器是否发起亲求就是根据是否有 src 属性决定的。

既然这样,那么我们就要对 <img /> 标签的 src 属性下手了,在没进入可视区域的时候,我们先不给这个 <img />标签赋 src 属性,这样岂不是浏览器就不会发送请求了。

我们知道了从 <img> 标签下手,那么更重要的就是可视区域的判断了。

4、与懒加载有关的 API

① 获取屏幕可视区的高度

document.documentElement.clientHeight

MDN:这个属性是只读属性,对于没有定义 CSS 或者内联布局盒子的元素为 0,否则,它是元素内部的高度(单位像素),包含内边距,但不包括滚动条、边框和外边距。

v2-ed3501641e343f3e67929f7ee13f359e_720w.jpg

② 获取元素相对于文档顶部的高度

element.offsetTop

MDN:为只读属性,它返回当前元素相对其 offsetParent 元素的顶部距离。

③ 获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离

document.documentElement.scrollTop

MDN:属性可以获取或设置一个元素的内容垂直滚动的像素数。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为 0。

④ 通过上面三个API,我们获得了三个值:可视区高度、元素相对于其父元素容器顶部的距离、浏览器窗口顶部与容器元素顶部的距离也就是滚动条滚动的高度。

v2-af1ab0c5f34e468e8647135c1f9f51e4_720w.jpg

⑥ 如果:offsetTop - scrollTop < clientHeight 则图片进入可视区内,则被请求。

五、代码实现

① 第一种实现方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            display: block;
            width: 100%;
            height: 300px;
            margin-bottom: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <img data-src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819216937,2118754409&fm=26&gp=0.jpg">
        <img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2654365388,3749485282&fm=26&gp=0.jpg">
        <img data-src="https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00487-1155.jpg">
        <img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=239110925,962980089&fm=26&gp=0.jpg">
        <img data-src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1808664313,530970200&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3325605116,3025793151&fm=26&gp=0.jpg">
        <img data-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1100786670,1609279816&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2665728391,1876846198&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1883571456,3298240469&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3065485140,2677122975&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2685177128,4134246955&fm=26&gp=0.jpg">
    </div>
    <script>
        var imgs = document.querySelectorAll("img");
        // 获取元素到顶部的距离
        function getPageOffset(el){
            var left = el.offsetLeft;
            var top =  el.offsetTop;
            while(el.offsetParent){
                el = el.offsetParent;
                left += el.offsetLeft + el.clientLeft;
                top += el.offsetTop + el.clientTop;
            }
            return { left, top };
        }
        // 图片懒加载
        function lazyLoad(imgs) {
            var clientH = document.documentElement.clientHeight;
            var scrollT = document.documentElement.scrollTop || document.body.scrollTop;
            for (let i = 0; i < imgs.length; i++) {
                var { top } = getPageOffset(imgs[i]);
                if (clientH + scrollT > top) {
                    imgs[i].src = imgs[i].getAttribute("data-src");
                }
            }
        }
        window.onload = window.onscroll = function() {
            lazyLoad(imgs);
        }
    </script>
</body>
</html>

② 第二种实现方式
利用 getBoundingClientRect() 来获取元素的大小及位置。
MDN:该方法返回元素的大小及其相对于是视口的位置。

v2-641fabfd753a1fa5f4749cc8d72d61b0_720w.jpg

实现方式:
懒加载的一个重点就是要知道什么时候图片是进入了可视区内,那么就上面这张图而言,我们有什么方法判断图片进入了可视区呢。
其实很简单:我们先获取图片到可视区顶部的距离,并获取到可视区的高度:

var bound = el.getBoundingClientRect();
var clientHeight = window.innerHeight;//这个和前面获取可视区高度的效果一样,只是兼容性问题

当我们滚动条向下滚动的时候,bound.top 值会变得越来越小,也就是图片到可视区顶部的距离也越来越小,当 bound.top == clientHeight 时,说明土片马上就要进入可视区了,只要我们在滚动,图片就会进入可视区,那么就需要请求资源了。也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。

代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            display: block;
            width: 100%;
            height: 300px;
            margin-bottom: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <img data-src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819216937,2118754409&fm=26&gp=0.jpg">
        <img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2654365388,3749485282&fm=26&gp=0.jpg">
        <img data-src="https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00487-1155.jpg">
        <img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=239110925,962980089&fm=26&gp=0.jpg">
        <img data-src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1808664313,530970200&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3325605116,3025793151&fm=26&gp=0.jpg">
        <img data-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1100786670,1609279816&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2665728391,1876846198&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1883571456,3298240469&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3065485140,2677122975&fm=26&gp=0.jpg">
        <img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2685177128,4134246955&fm=26&gp=0.jpg">
    </div>
    <script>
        var imgs = document.querySelectorAll("img");
        function isIn(img) {
            var top = img.getBoundingClientRect().top;
            var innerH = window.innerHeight;
            return top <= innerH;
        }
        function check(imgs) {
            [...imgs].forEach(img => {
                if (isIn(img)) {
                    lazyLoad(img);
                }
            })
        }
        function lazyLoad(img) {
            if (!img.src) {
                img.src = img.dataset.src;
            }
        }
        window.onload = window.onscroll = function() {
            check(imgs);
        }
    </script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容