参考资料:
- 《图片延迟加载3种实现方式》
- 《document.documentElement和document.body的区别》
- 《搞清clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop》
需求
页面中的图片位置默认显示loading.gif图像,只有当其首次出现在视口区域时,才显示其真正的图像内容,从而实现图片的懒加载功能,达到减少页面加载时间的目的。
基础知识
documentElement
document.documentElement
会返回文档对象(document
)的根元素(如HTML文档的<html>
元素)。offsetTop
Element.offsetTop
为只读属性,它返回当前元素相对于其offsetParent
元素的顶部内边距的距离。
-
scrollTop
Element.scrollTop
属性可以获取或设置这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离(像素数)。
当要获取网页中滚动条卷去部分的高度时,是用
document.documentElement.scrollTop
还是用document.body.scrollTop
呢?这要分情况处理:
- 页面具有DTD,或者说指定了DOCTYPE时,使用
document.documentElement
;- 页面不具有DTD,或者说没有指定了DOCTYPE时,使用
document.body
;因此为了兼容,不管有没有DTD,可以使用如下代码
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
-
clientHeight
Element.clientHeight
是只读属性,它会返回元素内部的高度(即可以看见的高度),包含内边距,但不包括水平滚动条、边框和外边距。
思路
- 实现功能的关键是使用图片的
offsetTop
与documentElement
元素的clientHeight
和scrollTop
这三个值来做判断,当满足下面的条件时,图片执行懒加载:
offsetTop < (scrollTop + clientHeight)
- 监听窗口的滚动事件
scroll
,触发图片懒加载的操作。 - 要用函数节流来做性能优化。
节流这块儿有必要多啰嗦几句。我已经实现图片懒加载了,为什么还要做函数节流,这不是多此一举吗?没有它,我的功能不是一样也能跑。
莫急,莫急,的确,不做节流,咱们的代码也能用,功能也不缺胳膊少腿,可以实现懒加载的需求。
但是,我们在实际使用时,每滚动一次鼠标的滚轮,滚动监听都会被触发多次。每触发一次,都要执行图片懒加载的操作,而这种DOM操作比起非DOM交互需要更多的内存和CPU时间,连续尝试过多的DOM相关操作可能会导致浏览器挂起,有时候会崩溃。
而函数节流背后的基本思想是:某些代码不可以在没有间断的情况下连续重复执行。使用函数节流的目的是只有在执行函数的请求停止了一段时间之后才执行。
所以,只要代码是周期性执行的,都应该使用节流。
函数节流的具体说明可以看红宝书的第614页,写得很好。
代码
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片懒加载</title>
</head>
<body>
<div class="main">
<div class="block">
<img src="../img/0.gif">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/1.gif">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/2.gif"">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/3.gif"">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/4.gif">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/5.gif"">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/6.gif"">
</div>
<div class="block">
<img class="loading" src="../assets/loading.gif" data-src="../img/7.gif">
</div>
</div>
<script>
let timeoutId = null;
const loadImgs = () => {
const imgs = document.querySelectorAll('img.loading');
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const seeHeight = document.documentElement.clientHeight;
imgs.forEach(img => {
if (img.offsetTop < (seeHeight + scrollTop)) {
img.setAttribute('src', img.getAttribute('data-src'));
img.classList.remove('loading');
} else { }
});
};
window.addEventListener('scroll', (event) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(loadImgs, 300);
});
window.onload = () => {
if (!timeoutId) {
loadImgs();
} else { }
};
</script>
</body>
</html>
效果如图所示:
通过观察右侧的开发者工具,可以看到,随着页面的滚动,图片实现了懒加载的功能。
--(完)--