# 懒加载实现
// 获取屏幕可视区域的高度 (不包括滚动条)
document.documentElement.clientHeight
1
2
2
mdn上的解释:
// 获取元素相对于文档顶部的高度
element.offsetTop
1
2
2
// 获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
document.documentElement.scrollTop
1
2
2
通过判断
element.offsetTop - document.documentElement.scrollTop < document.documentElement.clientHeight
来确定元素是否进入到了可视区域
<img data-src="./images/1.jpeg" alt="" />
<img data-src="./images/2.png" alt="" />
<img data-src="./images/3.png" alt="" />
<img data-src="./images/4.png" alt="" />
<img data-src="./images/5.png" alt="" />
<img data-src="./images/6.jpeg" alt="" />
<img data-src="./images/7.png" alt="" />
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>
<script>
let imgs = document.querySelectorAll('img');
function getTop(e) {
let T = e.offsetTop;
while ((e = e.offsetParent)) {
T += e.offsetTop;
}
return T;
}
function lazyLoad(imgs) {
let H = document.documentElement.clientHeight; // 获取可视区域高度
let S = document.documentElement.scrollTop || document.body.scrollTop;
imgs.forEach((item) => {
if (H + S > getTop(item)) {
item.src = item.getAttribute('data-src');
}
});
}
window.onload = window.onscroll = () => {
lazyLoad(imgs);
};
_.throttle(window.onscroll, 1000);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
这里用到了一个知识:
img标签在没有src属性的时候是不会去请求的。
# Element.getBoundingClientRect()
返回元素的大小及其相对于视口的位置(相对于可视区域左上角)
const bound = el.getBoundingClientRect();
1
当bound == clientHeight 时,说明元素即将进入可视区域了 。
所以bound <= clientHeight ,图片是在可视区域内的
bound<0 图片离开了可视区域
# Intersection Observer API
Intersection Observer API提供了一种异步检测目标元素与祖先元素或 viewport (opens new window) 相交情况变化的方法。
<img data-src="./images/1.jpeg" alt="" />
<img data-src="./images/2.png" alt="" />
<img data-src="./images/3.png" alt="" />
<img data-src="./images/4.png" alt="" />
<img data-src="./images/5.png" alt="" />
<img data-src="./images/6.jpeg" alt="" />
<script>
let imgs = document.querySelectorAll('img');
let callback = (e) => {
// console.log(e);
let el = e[0].target;
// set src attribute to load img
el.src = el.dataset.src;
};
let observer = new IntersectionObserver(callback);
imgs.forEach((img, i) => {
observer.observe(img);
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
参考文档:
https://zhuanlan.zhihu.com/p/55311726
Intersection Observer API (opens new window)
# 关于md-editor的图片懒加载
即v-html的图片懒加载
可以使用自定义指令
const lazyLoadDirective: ObjectDirective = {
mounted: (el: HTMLElement) => {
console.log('el: ', el)
nextTick(() => {
const imgObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement
img.src = img.dataset.src || ''
observer.unobserve(img)
}
})
})
const imgs = el.querySelectorAll('img')
console.log('imgs: ', imgs)
imgs.forEach((img) => {
img.dataset.src = img.src
img.src = ''
imgObserver.observe(img)
})
})
}
}
// 这里最重要的步骤是遍历imgs将图片的data-src赋值,并清空data属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
但是这么做也会有个问题
NetWork中会大量canceled的图片请求,虽然无关紧要,但是并不美观
因此最好是在v-html这一步就去设置data-src属性
这样,上面代码中的 img.dataset.src = img.src; img.src = ''这一步也就不需要了。