图片懒加载
window.echo = (function (window, document) {
'use strict';
/*
* 构造函数
*/
var Echo = function (elem) {
this.elem = elem;
this.render();
this.listen();
};
/*
* Images for echoing
*/
var echoStore = [];
/*
* 元素是否出现在视口
*/
var scrolledIntoView = function (element) {
var coords = element.getBoundingClientRect();
return (coords.top >= 0 && coords.left >= 0 && coords.top <= (window.innerHeight || document.documentElement.clientHeight));
};
/*
* 改变图片的src
*/
var echoSrc = function (img, callback) {
img.src = img.getAttribute('data-echo');
if (callback) {
callback();
}
};
/*
* 移除已经加载的图片
*/
var removeEcho = function (element, index) {
if (echoStore.indexOf(element) !== -1) {
echoStore.splice(index, 1);
}
};
/*
* Echo the images and callbacks
*/
var echoImages = function () {
for (var i = 0; i < echoStore.length; i++) {
var self = echoStore[i];
if (scrolledIntoView(self)) {
echoSrc(self, removeEcho(self, i));
}
}
};
/*
* Prototypal setup
*/
Echo.prototype = {
init : function () {
echoStore.push(this.elem);
},
render : function () {
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', echoImages, false);
} else {
window.onload = echoImages;
}
},
listen : function () {
window.onscroll = echoImages;
}
};
/*
* 初始化插件
*/
var lazyImgs = document.querySelectorAll('img[data-echo]');
for (var i = 0; i < lazyImgs.length; i++) {
new Echo(lazyImgs[i]).init();
}
})(window, document);
该脚本采用面向对象的方法,在for循环中的NodeList的每个元素实例上实例化Echo对象(它是Function构造函数)。您可以使用new运算符在脚本末尾看到此实例化。
第一个代码块是一个构造函数,依照规定,构造函数函数名应该大写
var Echo = function (elem) {
this.elem = elem;
this.render();
this.listen();
};
我传入了elem参数,它将是调用插件的for循环中的当前元素,并调用render()、listen()。在内部,这将运行Object继承的原型函数
接下来是一个空数组
var echoStore = [];
这个空数组将充当我们的数据存储,用于推送需要延迟加载的图像。对于这种类型的问题使用数组是一个很好的做法,因为我们可以删除已经从同一个数组加载的图像,这将阻止我们的循环遍历同一个数组,可以更快地执行并循环更少的项目。
接下来,一个简洁的小函数来检测元素是否在视图中:
var scrolledIntoView = function (element) {
var coords = element.getBoundingClientRect();
return (coords.top >= 0 && coords.left >= 0 && coords.top <= (window.innerHeight || document.documentElement.clientHeight));
};
使用getBoundingClientRect()法返回一个文本矩形对象,返回的数据描述了顶部,右侧,底部和左侧像素。然后我们可以对window.innerHeight或document.documentElement.clientHeight进行智能比较,它可以跨浏览器为您提供浏览器中的可见区域。
接下来是一个非常简单的函数,可以在需要时将当前图像的src属性切换到关联的data-echo属性:
var echoSrc = function (img, callback) {
img.src = img.getAttribute('data-echo');
if (callback) {
callback();
}
};
如果存在回调,它将运行(我在这里传递回调,但为了防止错误,只需声明这个东西就好了)。
我设置的下一个函数来检查当前元素是否存在于数组中,如果存在,则使用当前索引上的.splice()方法将其删除以删除“本身”:
var removeEcho = function (element, index) {
if (echoStore.indexOf(element) !== -1) {
echoStore.splice(index, 1);
}
};
该插件最主要的一点是基于我们的数据存储阵列在视图持续更新。此函数循环遍历我们的数据存储,并在启动scrolledIntoView函数后检查数组中的当前元素是否在视图中。如果证明是真的,那么我们调用echoSrc函数,传入当前元素以及当前元素的索引值,即i。此索引值将传递到removeEcho函数,该函数又从数组中删除自身的副本。这意味着我们的数组变得越来越短,我们的JavaScript在循环遍历我们的剩余元素时不必那么努力或长时间工作。
var echoImages = function () {
for (var i = 0; i < echoStore.length; i++) {
var self = echoStore[i];
if (scrolledIntoView(self)) {
echoSrc(self, removeEcho(self, i));
}
}
};
脚本的面向对象部分在原型扩展中查找,其中包含一些函数。第一个是init()函数,它只是将当前元素推送到我们的数据存储数组中。 render()函数检查是否存在addEventListener事件,然后在触发DOMContentLoaded事件后调用echoImages函数。如果它不存在,可能在IE7 / 8中,它只会运行onload。 listen()函数将在每次滚动窗口时再次运行该函数,以轮询并查看是否有任何元素进入视图以更多地发挥其魔力。
Echo.prototype = {
init : function () {
echoStore.push(this.elem);
},
render : function () {
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', echoImages, false);
} else {
window.onload = echoImages;
}
},
listen : function () {
window.onscroll = echoImages;
}
};
脚本的最后一部分是优雅的API,您可以在NodeList中的每个项目上调用新的Object:
var lazyImgs = document.querySelectorAll('img[data-echo]');
for (var i = 0; i < lazyImgs.length; i++) {
new Echo(lazyImgs[i]).init();
}
或者
[].forEach.call(document.querySelectorAll('img[data-echo]'), function (img) {
new Echo(img).init();
}