前言 🎤
经典问题,就不用多说
图片
<img data-src ='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
<img data-src ='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
<img data-src ='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
<img data-src ='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
<img data-src ='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>
方法一 offsetTop < clientHeight + scrollTop
{% iframe codepen.io/nishuzumi/e… %}
//HTML
<div style='height:500px'></div>
<div>
<img data-src ='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
<img data-src ='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
<img data-src ='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
<img data-src ='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
<img data-src ='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>
</div>
// css
img {
width:600px;
height:400px;
display:block
}
// js
function check(){
let imgs = document.querySelectorAll('img[data-src]')
if (imgs.length === 0) return
let viewHeight = document.documentElement.clientHeight
let offset = document.documentElement.scrollTop
imgs.forEach(img=>{
if(img.offsetTop < viewHeight + offset){
img.src = img.dataset.src
delete img.dataset.src
}
})
}
window.onscroll = check
check()
window.onscroll = check
check()
这个方法有个缺点,不能适应图片经过定位(position不为static)的父级元素。
方法二 getBoundingClientRect().top < clientHeight
{% iframe codepen.io/nishuzumi/e… %} html和css内容不变
function check(){
let imgs = document.querySelectorAll('img[data-src]')
if (imgs.length === 0) return
imgs.forEach(img=>{
if (img.getBoundingClientRect().top < document.documentElement.clientHeight){
img.src = img.dataset.src
delete img.dataset.src
}
})
}
window.onscroll = check
check()
性能稍微有点落后
方法三 InterscectionObserver
let observe = new IntersectionObserver(
function (entities) {
entities.forEach(entity => {
if(entity.intersectionRatio <= 0) return
let img = entity.target
img.src = img.dataset.src
delete img.dataset.src
observe.unobserve(img)
})
}, {
threshold: 0.01
})
document.querySelectorAll('img').forEach(img => { observe.observe(img) })
⚠️注意,第一次监听元素的时候会直接触发一次callback,如果有多个元素,则entities是一个数组。
方法四 loading='lazy'
<img loading='lazy' src='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>
使用loading='lazy'的时候,他的行为会稍微有些怪异。他不会等到距离视窗非常近的时候才加载,而是在有一段距离时就开始提前加载。