這個(gè)問(wèn)題困擾我有一段時(shí)間了疼燥。對(duì)于整個(gè)Document
的加載可以通過(guò)load
或DOMContentLoaded
事件來(lái)判斷璃岳,而對(duì)于某個(gè)DIV我們?cè)撊绾潍@取加載完成的通知呢谓晌?
用一個(gè)問(wèn)題場(chǎng)景來(lái)說(shuō)明一下杯拐。頁(yè)面初始化時(shí)通過(guò)腳本加載了一個(gè)DIV
互拾,通過(guò)display:none;
隱藏起來(lái),當(dāng)需要的時(shí)候再顯示萧落。這樣一來(lái)DIV
脫離了文檔流坛吁,瀏覽器不會(huì)計(jì)算其大小和位置。如果DIV
內(nèi)部有一個(gè)子模塊需要通過(guò)計(jì)算容器的大小進(jìn)行繪制铐尚,只能通過(guò)顯示這個(gè)DIV
的時(shí)點(diǎn)觸發(fā)一個(gè)消息來(lái)進(jìn)行繪制。而在某些模塊化設(shè)計(jì)的場(chǎng)景下哆姻,我們需要子模塊更加內(nèi)聚宣增,去相應(yīng)這樣一個(gè)外部事件是不合適的。我們希望在這個(gè)DIV
元素被繪制的時(shí)候再進(jìn)行子模塊的繪制矛缨。
經(jīng)過(guò)對(duì)MDN手冊(cè)的逐條閱讀后爹脾,我找到了一個(gè)方法 :
Element.getClientRects()
這個(gè)方法返回元素繪制后的DomRect
對(duì)象帖旨。如果元素沒(méi)有繪制則返回null。找到了這個(gè)關(guān)鍵方法后灵妨,我們需要建立一個(gè)通知機(jī)制解阅。我選用Promise
。代碼如下:
$.fn.extend({
'waitLayout':function(){
var self=this;
return new Promise(function(resolve,reject){
var _fn_check_layout=function(){
var rect_list=self[0].getClientRects();
if(rect_list.length===0){
window.setTimeout(_fn_check_layout,100);
}else{
resolve(rect_list[0]);
}
};
window.setTimeout(_fn_check_layout);
});
}
});
//測(cè)試1-直接加載到主DOM樹(shù)
var div1=$("<div></div>");
div1.waitLayout().then((rect)=>{
console.log("加載完成",rect);
});
$("body").append(div1); //輸出 "加載完成 DOMRect..."
//測(cè)試2-加載到主DOM樹(shù)時(shí)隱藏泌霍,然后顯示
var div2=$("<div></div>");
div2.waitLayout().then((rect)=>{
console.log("加載完成",rect);
});
div2.css("display","none");
$("body").append(div2);
div2.css("display","unset"); //輸出 "加載完成 DOMRect..."
//測(cè)試2-加載到隱藏的父節(jié)货抄,然后顯示父節(jié)點(diǎn)
var div3=$("<div></div>");
var div3_container=$("<div></div>");
div3.waitLayout().then((rect)=>{
console.log("加載完成",rect);
});
div3_container.css("display","none");
$("body").append(div3_container);
div3_container.append(div3);
div3_container.css("display","unset"); //輸出 "加載完成 DOMRect..."
這個(gè)方法有一個(gè)缺陷。setTimeout
循環(huán)有可能導(dǎo)致性能問(wèn)題朱转。我們用100個(gè)元素來(lái)測(cè)試一下蟹地。
for(var i=0;i<100;i++){
var div=$("<div></div>");
div.waitLayout().then((rect)=>{
console.log("加載完成",rect);
});
}
性能顯示如下: