前言
在写一个懒加载插件时,遇到一个坑,就是this的指向问题,我想这种情况大部分人都会遇到,就写下来,新手也有个参考。
事件
有些页面图片比较多,但用户还不一定会全看,这样的话,全部去加载这些图片,就有点浪费资源了,于是想写一个通用的插件来解决这个问题
想法
根据滚动条高度加屏幕高度来判断是否加载这张图片,需要懒加载的图片这样写
<img data-src="image/bg.jpg">
凡是加上这个属性的都会做懒加载处理。
this之坑
以下是最终的代码
function SetImg(top){
var imgs = Array.prototype.slice.apply(document.getElementsByTagName("img"));
this.imgs = imgs.filter(function(item,index){
return item.dataset.src;
});
this.top = top || 150;
}
SetImg.prototype = {
init:function(){
this.event();
},
setSrc:function(){
if(this.imgs.length===0){
window.removeEventListener("scroll",this.setSrc);
};
var _this = this;
this.imgs.forEach(function(item,index){
if(document.documentElement.clientHeight+document.body.scrollTop+_this.top>item.offsetTop||item.offsetTop<document.documentElement.clientHeight){
item.src = item.dataset.src;
_this.imgs.splice(index,1);
}
})
},
event:function(){
this.setSrc = this.setSrc.bind(this);
window.addEventListener("load",this.setSrc);
window.addEventListener("scroll",this.setSrc);
}
};
new SetImg().init();
这个代码已经解决了this的指向问题,也就是下面这句
this.setSrc = this.setSrc.bind(this);
一开始是没有这句话的,但问题是,我在setSrc里面使用了this,而恰恰那里面的this指向的是window,为什么指向window?因为这句话
window.addEventListener("load",this.setSrc);
window.addEventListener("scroll",this.setSrc);
我们说this始终指向一个对象,而现在给添加的事件,就是在window身上添加的,正如
element.addEventListener("click",fn);
这句不就是指向element吗,那上面的那个指向window,也就不足为奇了。因此这也是为什么最后我添加这么一句
this.setSrc = this.setSrc.bind(this);
其实一开始我没想要这么做,但这也是迫不得已的事,一开始我是这样写的:
window.addEventListener("load",this.setSrc.bind(this));
window.addEventListener("scroll",this.setSrc.bind(this));
但这样的问题是,bind返回的是一个新对象,而不是原本的this.setSrc。一般情况下,这也不是什么大问题,但坑就坑在this.setSrc里面的这句
if(this.imgs.length===0){
window.removeEventListener("scroll",this.setSrc);
};
看似一切都正常,但这是一个大坑,这里面的this.setSrc指向的是SetImg.setSrc,而
window.addEventListener("scroll",this.setSrc.bind(this));
this.setSrc.bind(this)这是一个新对象,因此你根本就无法remove掉这个新对象。所以最终才想出个迫不得已的方法就是让this.setSrc变成新的那个对象。
可能有些朋友,不太懂为什么要写这么一句:
if(this.imgs.length===0){
window.removeEventListener("scroll",this.setSrc);
};
这句话的作用是,如果所有图片都加载完毕了,这个滚动事件,就不需要了。当然如果你直接使用window.onscroll这种情势来写,或许这个问题可以很好的解决,但作为一个插件用addEventListener是迫不得已的,因为我不知道哪个页面使用了onscroll事件,如果我直接那样写,就会把其他人写的事件覆盖掉。
结语
这种情况出现的概率还是蛮高的,导致这种问题的出现就是,事件里面的this和构造函数里面的this,指向的是不同的对象,所以啊这就是坑点。
说到正题,这个插件还不能用T_T,再改改吧