前言
在寫一個懶加載插件時,遇到一個坑,就是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,再改改吧