A: 如何实现 Lazy Load?

董俊
2023-12-01

最近面试了几家公司,他们不约而同都问到了这个问题:了解 Lazy Load 吗?

Lazy Load is delays loading of images in long web pages. Images outside of viewport are not loaded until user scrolls to them. This is opposite of image preloading.

Using Lazy Load on long web pages will make the page load faster. In some cases it can also help to reduce server load.

Lazy Load其实是对图片的一种延迟加载技术,直到用户滚动图片出现在用户可视范围时才把它加载出来。它与图片预加载技术完全相反,却都是为了提高用户体验而设计。

Lazy Load Plugin for jQuery - Mika Tuupola

jQuery的Lazy Load插件大家应该都有了解或者已经使用过了。下面是一个简单的栗子:


    <img class="lazy" data-original="img/example.jpg" width="640" height="480">
    
    $(function() {
        $("img.lazy").lazyload();//可以传入自定义的参数
    });

data-original属性值是我们想要显示的图片的URL,当用户滚动页面,图片出现在视线范围内时,替换img元素的src属性值为data-original属性值。

不过仅仅了解这些好像远远不够。我在网上查找了一些Lazy Load的实现原理,发现了以下的代码:

    // 原生JS实现方法
    <script>
        var imgs = document.getElementsByTagName('img');
        // 获取视口高度与滚动条的偏移量
        function lazyload(){
            // 获取当前滚动条偏移量
            var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
            // 获取视口高度
            var viewportSize = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
            for(var i=0; i<imgs.length; i++) {
                var x =scrollTop+viewportSize-imgs[i].offsetTop;
                if(x>0){
                    imgs[i].src = imgs[i].getAttribute('loadpic');   
                }
            }
        }
        setInterval(lazyload,1000);
    </script>

这位老铁自己写了一段简陋的实现代码,他用了定时器循环调用lazyload方法,当然大家可以先忽略,重点看一下如何获取当前滚动条的位置和视口的高度。

    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;

这个东西我刚看也是一头雾水。我在chrome上亲身实践了一下,然而发现,document.documentElement.scrollTop总是返回0,其他两种方法都正常获取到了值。于是查了一下才知道这三个获取滚动条位置的方法背后还有故事。看这个问题:

document.documentElement.scrollTop return value differs in Chrome

还有其中一位老铁的回答:

The standards-based way of getting the scroll is window.scrollY. This is supported by Chrome, Firefox, Opera, Safari and IE Edge or later. If you only support these browsers, you should go with this property.

IE >= 9 supports a similar property window.pageYOffset, which for the sake of compatibility returns the same as window.scrollY in recent browsers, though it may perhaps be deprecated at some point.

The problem with using document.documentElement.scrollTop or document.body.scrollTop is that the scroll needn't be defined on either of these. Chrome and Safari define their scroll on the <body> element whilst Firefox defines it on the <html> element returned by document.documentElement, for example. This is not standardized, and could potentially change in future versions of the browsers. However, if the scrollY or pageYOffset are not present, this is the only way to get the scroll.

window.scrollY || window.pageYOffset || document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0)

那这故事我就不讲了。

    var viewportSize = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

这行代码是为了获取视口的高度,我们同样看到了三种方法,那他们背后肯定也有不为我知的故事,现在我也不太想知道了。

    var x =scrollTop+viewportSize-imgs[i].offsetTop;

如何获得一个元素与页面顶端的距离,你学会了吗?

下面是用jQuery实现的lazyload,也是刚才那位老铁写的:

    /**
    * 图片的src实现原理
    */
    $(document).ready(function(){
        // 获取页面视口高度
        var viewportHeight = $(window).height();
        var lazyload = function() {
            // 获取窗口滚动条距离
            var scrollTop = $(window).scrollTop();
            $('img').each(function(){
            // 判断 视口高度+滚动条距离 与 图片元素距离文档原点的高度         
            var x = scrollTop + viewportHeight - $(this).position().top;
            // 如果大于0 即该元素能被浏览者看到,则将暂存于自定义属性loadpic的值赋值给真正的src            
            if (x > 0)
            {
                $(this).attr('src',$(this).attr('loadpic')); 
            }
        })
        }
        // 创建定时器 “实时”计算每个元素的src是否应该被赋值
        setInterval(lazyload,100);
    });

上述的实现呢,我感觉还是可以说服我的,把定时器去掉,加入对滚动事件的侦听即可。不过,就这么草草了事好像也没什么意思,我下载了jQuery-lazyload的源码,准备研究一波。如果我看懂了什么,或者有什么收获,再来聊聊。

    /*!
     * Lazy Load - jQuery plugin for lazy loading images
     *
     * Copyright (c) 2007-2015 Mika Tuupola
     *
     * Licensed under the MIT license:
     *   http://www.opensource.org/licenses/mit-license.php
     *
     * Project home:
     *   http://www.appelsiini.net/projects/lazyload
     *
     * Version:  1.9.7
     *
     */
    (function($, window, document, undefined) {
      // body...
    })(jQuery, window, document);
 类似资料: