本文参照了博文:http://axuebin.com/blog/2017/08/19/javascript-lazyload/ 在他的基础上做了一些改动
因为一个图片就是一个请求,请求多了,自然浏览器的渲染速度下降,图片懒加载就是解决这个问题的一个方法。还有一个方法是使用雪碧图,可以看我另外一个blog。
所谓的图片懒加载就是一开始就不加载所有的图片,而是当我们滚动到图片所在的区域的时候,再加载它。
标签有一个属性是
src
,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有src
属性,就不会发送请求。我先不设置src
,等滚动到该图片的时候再用js设置他的src,把图片真正的URL放在另一个属性data-src
中,在需要的时候也就是图片进入可视区域的之前,将URL取出放到src
中。
通过document.documentElement.clientHeight
获取屏幕可视窗口高度
通过element.offsetTop
获取元素相对于文档顶部的距离
通过document.documentElement.scrollTop
获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
由上图可知如果offset - scrollTop(差值)的大小大于clientHeight说明还没有到图片所在位置,如果小于说明已经下拉到图片了,为负值说明图片已经被拖动到视口之上了。
//文档加载好之后就检查有无图片需要加载
window.onload = checkImgs;
// document.ready=checkImgs;
//滚动后触发节流函数
window.onscroll = throttle;
//检查图片是否进入可视区域
function isInSight(el) {
console.log(el);
const clientHeight = document.documentElement.clientHeight;
const offsetTop = el.offsetTop;
const scrollTop = document.getElementById('root').scrollTop;
//下面等式右端加了一个50 作用是稍微提前一点加载图片 而不是看到了图片才进行加载 这样会影响体验
return (offsetTop - scrollTop) <= clientHeight + 50;
}
//对所有所有没有加载的图片进行检查
// 这里全局设置的index作用是记录页面加载到了第几张图片了,防止多次加载已经加载好了的图片
let index = 0;
function checkImgs() {
const imgs = document.getElementsByClassName('my-photo');
for (let i = index; i < imgs.length; i++) {
if (isInSight(imgs[i])) {
loadImg(imgs[i]);
index = i;
}
}
}
//加载图片src
function loadImg(el) {
if (!el.src) {
const source = el.dataset.src;
el.src = source;
}
}
//这里使用了函数节流,防止多次触发,影响效率
var canrun = true;
function throttle() {
if (!canrun) {
return;
}
canrun = false;
setTimeout(() => {
checkImgs();
canrun = true;
}, 200);
}
1.1 clientHeight:包括padding但不包括border、水平滚动条、margin的元素的高度。对于inline的元素这个属性一直是0,单位px,只读元素。在本博文需要用到可视窗口的高度,那么我们就使用window.clientHeight这个属性即可。
1.2 clientTop/clientLeft/clientRight/clientBottom
clientTop:元素上边框的厚度,当没有指定边框厚底时,一般为0。
2.1 scrollHeight:当本元素的子元素比本元素高且overflow=scroll时,本元素会scroll,这时:
scrollHeight: 因为子元素比父元素高,父元素不想被子元素撑的一样高就显示出了滚动条,在滚动的过程中本元素有部分被隐藏了,scrollHeight代表包括当前不可见部分的元素的高度。而可见部分的高度其实就是clientHeight,也就是scrollHeight>=clientHeight恒成立。在有滚动条时讨论scrollHeight才有意义,在没有滚动条时scrollHeight==clientHeight恒成立。单位px,只读元素。
2.2 scrollTop/scrollLeft/scrollRight/scrollBottom
scrollTop:代表在有滚动条时,滚动条向下滚动的距离也就是元素顶部被遮住部分的高度。在没有滚动条时scrollTop==0恒成立。单位px,可读可设置。
一个元素的 scrollTop
值是这个元素的顶部到视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。
3.1 offsetheight:包括padding、border、水平滚动条,但不包括margin的元素的高度。对于inline的元素这个属性一直是0,单位px,只读元素。
3.2 offsetTop/offsetLeft/offsetRight/offsetBottom
offsetTop:当前元素顶部距离最近父元素顶部的距离,和有没有滚动条没有关系。单位px,只读元素。
之前看了很多文章把函数防抖和函数节流说的很复杂也很不清楚,其实很简单的两个东西。
函数防抖:触发后会在你所设定的间隔时间完成后执行相关的代码,如果这个时候你再次触发了,那么你必须得重新再等一次间隔时间。间隔时间内不允许被打断!打断后重来!
你可以想成是一个单机游戏打boss,boss每一个10min出现一次,但是这个时间你要是退出游戏了,再次登陆那么又必须再重新等一个10min,而不是接着之前的继续等。
// 函数防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
timer = setTimeout(function(){
console.log("函数防抖");
}, 300);
};
函数节流:函数节流和函数防抖的区别在于,函数节流允许被打断。触发函数节流后,必定在设定的间隔时间内执行一次,哪怕你在这中间无数次打断。
可以想成是一个联网游戏打boss,boss每一个10min出现一次,9点开始计时,9:05你退出游戏了,9:07再次登陆,只需再等个3min boss就刷出来了。
// 函数节流
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
// 判断是否已空闲,如果在执行中,则直接return,而并不会置零计时器
return;
}
canRun = false;
setTimeout(function(){
console.log("函数节流");
canRun = true;
}, 300);
};
1 ScrollTop一直为0
之前那篇博文在scrollTop这里有一点不对的地方,这里的scroll最好不要使用body里面的元素,因为scrollTop的定义是带有滚动条的元素这个属性才有值,不然都为0。一开始我用root的scrolltop发现不能实现懒加载,问题就出在root的scrollTop始终为0的问题上。我把root换成body之后就解决了这个问题。
2 window.onload & document.ready