JavaScript 实现图片延迟加载的三种原生方法详解
图片延迟加载(Lazy Load)是前端性能优化的重要手段,尤其适用于图片较多的长页面。其核心原理是:页面初始只加载可视区域内的图片,当用户滚动页面时,检测到目标图片进入可视区域后,再将其真实地址赋值给 src 属性进行加载。本文将详细解析三种基于原生 JavaScript 的实现方案。
基础方法:监听滚动与 DOM 遍历
这是最直观的实现方式。通过监听 window 的 scroll 事件,获取当前滚动高度和屏幕可视高度,遍历所有标记为需延迟加载的图片元素,计算其距离页面顶部的偏移量。若该偏移量小于“滚动高度 + 屏幕高度”,说明图片已进入可视范围,此时将自定义属性(如 alt 或 data-src)的值赋给 src 并移除标记类名。
<script type="text/javascript">
var temp = -1; // 记录上次滚动位置,用于判断滚动方向
window.onscroll = function() {
var imgElements = document.getElementsByTagName("img");
var lazyImgArr = [];
// 筛选出带有 'lazy' 类的图片
for(var i=0; i<imgElements.length; i++) {
if(imgElements[i].className == "lazy"){
lazyImgArr.push(imgElements[i]);
}
}
var scrollHeight = document.body.scrollTop || document.documentElement.scrollTop;
var bodyHeight = document.documentElement.clientHeight;
// 仅向下滚动时才执行检测
if(temp < scrollHeight) {
for(var k=0; k<lazyImgArr.length; k++) {
var imgTop = lazyImgArr[k].offsetTop;
// 判断图片顶部是否在当前可视区域内
if((imgTop - scrollHeight) <= bodyHeight) {
// 将真实图片路径赋给 src
lazyImgArr[k].src = lazyImgArr[k].alt;
lazyImgArr[k].className = "notlazy";
}
}
temp = scrollHeight;
}
};
</script>
关键点:需要确保图片初始 src 为一个极小的占位图,真实路径可存储在 alt 或其他自定义属性中。此外,必须处理向上滚动的情况以避免重复计算。
进阶封装:IIFE 模式与队列管理
为了提升代码的可维护性和复用性,可以将逻辑封装在一个立即执行函数表达式(IIFE)中。该方法使用 map_element 对象,以图片距文档顶部的绝对距离作为 key,保存待加载元素的索引数组。这种方式将元素按垂直位置分组,便于批量处理。
lazyLoad = (function() {
var map_element = {}; // { position: [index1, index2] }
var element_obj = []; // 存储所有待加载元素引用
var download_count = 0; // 剩余待加载数量
var last_offset = -1; // 上次处理的视口底端位置
var doc_body;
var doc_element;
var lazy_load_tag = ["img", "iframe"];
function initVar() {
doc_body = document.body;
doc_element = document.compatMode == 'BackCompat' ? doc_body : document.documentElement;
};
function getAbsoluteTop(element) {
var offsetTop = element.offsetTop;
while (element = element.offsetParent) {
offsetTop += element.offsetTop;
}
return offsetTop;
}
function initElementMap() {
var all_elements = [];
// 收集所有带 lazy_src 属性的元素
for (var i = 0; i < lazy_load_tag.length; i++) {
var el = document.getElementsByTagName(lazy_load_tag[i]);
for (var j = 0; j < el.length; j++) {
if (el[j].getAttribute("lazy_src")) {
all_elements.push(el[j]);
}
}
}
// 按绝对位置分组
for (var i = 0; i < all_elements.length; i++) {
var o_img = all_elements[i];
var t_index = getAbsoluteTop(o_img);
element_obj.push(o_img);
if (!map_element[t_index]) {
map_element[t_index] = [];
download_count++;
}
map_element[t_index].push(i);
}
};
function checkAndDownload() {
if (download_count === 0) return;
var scrollTop = window.pageYOffset !== undefined ? window.pageYOffset : (document.compatMode=="CSS1Compat" ? document.documentElement.scrollTop : document.body.scrollTop);
var clientHeight = document.documentElement.clientHeight;
var viewBottom = scrollTop + clientHeight;
// 如果视口底端没有变化,避免频繁触发
if (last_offset == viewBottom) {
setTimeout(checkAndDownload, 200);
return;
}
last_offset = viewBottom;
// 检查并加载进入视口的元素
for (var key in map_element) {
if (viewBottom > key) { // key 是元素顶部位置
var indices = map_element[key];
for (var l = 0; l < indices.length; l++) {
var target = element_obj[indices[l]];
target.src = target.getAttribute("lazy_src");
}
delete map_element[key];
download_count--;
}
}
setTimeout(checkAndDownload, 200);
};
function init() {
initVar();
initElementMap();
checkAndDownload();
};
return { init: init };
})();
// 使用方式:页面底部调用 lazyLoad.init();
优势:通过定时器递归调用而非直接绑定 scroll 事件,结合 last_offset 判断,有效减少了高频事件触发的开销。同时支持 iframe 等多类媒体元素的延迟加载。
优化方案:IntersectionObserver 思维的原生简化版
虽然 IntersectionObserver API 是现代浏览器推荐的方式,但在不支持的环境或需要兼容旧版环境时,我们可以采用更简洁的事件委托思路。此方法利用 data-url 属性存储真实图片链接,初始 src 为占位图。通过初始化检查和滚动监听相结合,确保首屏图片不被遗漏。
<script type="text/javascript">
(function(){
var v = {};
// 获取元素集合
function tagName(tagName){
return document.getElementsByTagName(tagName);
}
// 统一添加事件监听
function addEvent(obj, type, func){
if(obj.addEventListener){
obj.addEventListener(type, func, false);
} else if(obj.attachEvent){
obj.attachEvent('on'+type, func);
}
}
function loadImages(elements) {
var screenHeight = document.documentElement.clientHeight;
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
var limitHeight = scrollTop + screenHeight;
for(var i=0; i<elements.length; i++){
var img = elements[i];
// 判断是否在可视区域且有未加载标志
if(img.offsetTop <= limitHeight && img.getAttribute('data-url')){
img.setAttribute('src', img.getAttribute('data-url'));
img.removeAttribute('data-url');
}
}
}
// 初始化:加载首屏可见图片
function init(){
var imgs = tagName('img');
loadImages(imgs);
// 后续滚动继续加载
var scrollTimer = null;
addEvent(window,'scroll', function(){
clearTimeout(scrollTimer);
scrollTimer = setTimeout(function(){
loadImages(imgs);
}, 150); // 简单的节流处理
});
}
init();
})()
</script>
注意事项:在动态切换内容(如 Tab 页签切换)的场景下,新显示区域的图片可能不会自动触发加载。此时需要在内容切换完成后,手动执行类似 target.find("img[data-url]").each(...) 的处理逻辑,即时渲染新插入的图片。
总结
以上介绍了三种不同复杂度的 JavaScript 图片延迟加载实现方案。从基础的 DOM 遍历到模块化封装,再到针对动态内容的处理技巧,开发者可根据项目实际需求选择合适的方法。无论采用哪种方案,核心都在于准确判断元素与视口的位置关系,并在合适的时机触发资源加载。
以上就是 JavaScript 实现图片延迟加载方法汇总的详细内容,更多关于 JavaScript 前端优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













