纯JavaScript实现图片延时加载的两种方案
针对长页面图片加载导致的性能问题,本文介绍两种纯JavaScript实现图片延时加载(懒加载)的方案。第一种方案利用自定义属性csii_src延迟赋值src;第二种方案通过class状态切换及alt属性预存路径实现。文章详细解析了核心逻辑、代码实现及注意事项,旨在帮助开发者优化页面加载速度并节省流量。
背景与需求分析
近期整理了一些以往开发中实用的插件,特此分享。
现代网页内容日益丰富,尤其是淘宝、京东等大型电商网站,首页作为核心流量入口,其加载性能至关重要。
在近期开发的某银行网站项目中,首页曝光位置竞争激烈,各业务部门对首页布局提出了多样化需求。
为满足各方需求,技术负责人指出首页资源体积过大,要求必须进行性能优化与精简。
首要措施是压缩图片大小,若仍不达标,则需采用更高效的加载策略。对于长页面中当前视口未显示区域的图片,可以暂缓加载,这既能节省用户流量,也能降低服务器带宽成本。
方案一:自定义属性延迟加载
核心思路是计算图片在页面中的位置坐标,若图片顶部距离大于当前屏幕可视高度,则暂不加载,否则立即显示。
实现初始隐藏的关键在于使用浏览器不识别的自定义属性,例如<img csii_src="1.png">。当触发加载条件时,通过element.src=element.getAttribute("csii_src");将自定义属性值赋给src,从而触发图片加载。
function lazyLoad() {
var map_element = {};
var element_obj = [];
var download_count = 0;
var last_offset = -1;
var doc_body = null;
var doc_element = null;
var lazy_load_tag = [];
function initVar(tags) {
doc_body = document.body;
doc_element = document.compatMode == 'BackCompat' ? doc_body
: document.documentElement;
lazy_load_tag = tags || [ "img", "iframe" ];
}
;
function initElementMap() {
for ( var i = 0, len = lazy_load_tag.length; i < len; i++) {
var el = document.getElementsByTagName(lazy_load_tag[i]);
for ( var j = 0, len2 = el.length; j < len2; j++) {
if (typeof (el[j]) == "object"
&& el[j].getAttribute("csii_src")) {
element_obj.push(el[j]);
}
}
}
for ( var i = 0, len = element_obj.length; i < len; i++) {
var o_img = element_obj[i];
var t_index = getAbsoluteTop(o_img);
if (map_element[t_index]) {
map_element[t_index].push(i);
} else {
var t_array = [];
t_array[0] = i;
map_element[t_index] = t_array;
download_count++;
}
}
}
;
function initDownloadListen() {
if (!download_count)
return;
/*var offset = (window.MessageEvent && !document.getBoxObjectFor) ? doc_body.scrollTop
: doc_element.scrollTop;*/
var offset;
if(os.firefox){
offset = doc_element.scrollTop;
}else{
offset = doc_body.scrollTop;
}
var visio_offset = offset + doc_element.clientHeight;
if (last_offset == visio_offset) {
// setTimeout(initDownloadListen, 200);
return;
}
last_offset = visio_offset;
var visio_height = doc_element.clientHeight;
var img_show_height = visio_height + offset + 20;
for ( var key in map_element) {
if (img_show_height > key) {
var t_o = map_element[key];
var img_vl = t_o.length;
for ( var l = 0; l < img_vl; l++) {
element_obj[t_o[l]].src = element_obj[t_o[l]]
.getAttribute("csii_src");
}
delete map_element[key];
download_count--;
}
}
// setTimeout(initDownloadListen, 200);
}
;
function getAbsoluteTop(element) {
if (arguments.length != 1 || element == null) {
return null;
}
var offsetTop = element.offsetTop;
while (element = element.offsetParent) {
offsetTop += element.offsetTop;
}
return offsetTop;
}
function init(tags) {
initVar(tags);
initElementMap();
initDownloadListen();
$(window).scroll(function(){
initDownloadListen();
});
}
;
init();
}
该方案存在局限性:页面结构必须固定,且图片高度需预先设定,否则无法准确计算图片距离顶部的偏移量。
方案二:Class状态切换与占位图
接下来介绍另一种常见的实现方式。
<!--
var temp = -1;//用来判断是否是向下滚动(向上滚动就不需要判断延迟加载图片了)
window.onscroll = function() {
var imgElements = document.getElementsByTagName("img");
var lazyImgArr = new Array();
var j = 0;
for(var i=0; i<imgElements.length; i++) {
if(imgElements[i].className == "lazy"){
lazyImgArr[j++] = imgElements[i];
}
}
var scrollHeight = document.body.scrollTop;//滚动的高度
var bodyHeight = document.body.offsetHeight;//body(页面)可见区域的总高度
if(temp < scrollHeight) {//为true表示是向下滚动,否则是向上滚动,不需要执行动作。
for(var k=0; k<lazyImgArr.length; k++) {
var imgTop = lazyImgArr[k].offsetTop;//1305(图片纵坐标)
if((imgTop - scrollHeight) <= bodyHeight) {
lazyImgArr[k].src = lazyImgArr[k].alt;
lazyImgArr[k].className = "notlazy"
}
}
temp = scrollHeight;
}
};
// -->
该方案在<img>标签中将src设置为一个极小的占位图片路径,并将alt属性设置为真实图片路径。当用户滚动至图片可视区域时,将src替换为alt的值以加载真实图片。同时,通过给标签添加值为lazy的class来标记待加载图片,加载完成后将class值改为notlazy,以此区分已加载和未加载状态。
代码中的注释已详细说明逻辑,欢迎提出改进建议。
tween.js 内存泄漏预防:怎么正确停止动画与清理对象
2026-11-05 15:52:56
Vue或React项目中为何不建议直接使用JS刷新页面
2026-11-05 13:57:30
哪里可以找到高质量的免费JS脚本资源?
2026-11-05 13:15:03
JS数组对象怎么根据指定ID字段去重
2026-11-04 21:41:10
JS数组去重方法性能对比:Set、Map与Filter谁更快
2026-11-04 16:14:15
