如何实现懒加载?
懒加载最常用在图片上,其他的元素也有使用,比如 iframe,页面甚至组件
- 图片
scroll
实现IntersectionObserver
实现
- iframe
- 页面
- 组件
实现懒加载
[clientHeight、scrollTop 和 offsetTop]
js
// 方案一:clientHeight, scrollTop 和 offsetTop
// 通过监听 scroll 事件来判断图片是否到达视口
let img = document.getElementsByTagName('img')
let num = img.length
let count = 0 // 计数器,从第一张图片开始计
lazyload() // 首次加载别忘了显示图片
window.addEventListener('scroll', lazyload)
function getScrollTop() {
// 首先尝试使用 window.pageYOffset(兼容大多数主流浏览器)
// 如果不支持,则尝试 document.documentElement.scrollTop(滚动操作作用于根元素,即 `<html>` 元素)
// 最后再尝试 document.body.scrollTop,因为在某些老旧的浏览器中,例如 IE8 及更早版本,滚动操作作用于 `<body>` 元素而不是根元素。
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
}
function lazyload() {
// 视口高度
let viewHeight = document.documentElement.clientHeight
// 滚动条已滚动的高度
let scrollTop = getScrollTop()
for (let i = count; i < num; i++) {
// 元素现在已经出现在视口中
if (img[i].offsetTop < scrollHeight + viewHeight) {
if (img[i].getAttribute('src') !== 'default.jpg') continue
img[i].src = img[i].getAttribute('data-src')
count++
}
}
}
// 最好对 scroll 事件做节流处理,以免频繁触发:
window.addEventListener('scroll', throttle(lazyload, 200))
js
// 用另外一种方式来判断图片是否出现在了当前视口,即 DOM 元素的 getBoundingClientRect API
// 根据当前元素的位置和视口进行判断是否要加载这张图片
// 性能优化
// - 图片全部加载完成后移除事件监听
// - 加载完的图片,从 imgList 移除,起到优化内存的作用
// https://juejin.cn/post/6844903856489365518#heading-19
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length
const imgLazyLoad = function () {
let count = 0
const clientHeight = document.documentElement.clientHeight
return (function () {
let deleteIndexList = []
imgList.forEach((img, index) => {
let rect = img.getBoundingClientRect()
if (rect.top < window.innerHeight) {
// 判断逻辑 也可以使用 clientHeight
img.src = img.dataset.src
deleteIndexList.push(index)
count++
if (count === length) {
document.removeEventListener('scroll', imgLazyLoad)
}
}
})
imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
})()
}
// 这里最好加上防抖处理
document.addEventListener('scroll', imgLazyLoad)
js
// lazy-load iframe
// 基础用法
// create a new Intersection Observer
;(function () {
const callback = () => {}
const observer = new IntersectionObserver(callback)
})()
// the element that you want to watch
// const element = document.querySelector('iframe');
// register the element with the observe method
// observer.observe(element);
;(function () {
const observer = new IntersectionObserver(function (entries, observer) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 判断是否在视口中
// do this when the element enters the viewport
loadElement(entry.target)
// stop watching
observer.unobserve(entry.target)
}
})
})
function loadElement(element) {
const src = element.getAttribute('data-src')
element.src = src
}
})()