一、廣告代碼分析
很多第三方的廣告系統都是使用document.write來加載廣告,如下面的一個javascript的廣告鏈接。
複製代碼代碼如下:
<script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"></script>
這個javascript請求返回的是這樣的一段代碼:
複製代碼代碼如下:
document.write( "<a href='http://gg.5173.com/adpolestar/wayl/;" +
"ad=6FF3F844_33E6_86EE_3B96_D94C1CF1AEC4;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;" +
"pu=5173;/?http://www.7bao.com/g/xlsbz/index' target='_blank'><img src='" +
"http://html.5173cdn.com/market/yunyinga/xly132.gif' " +
"border='0' /></a>" );
這種看似有點二的加載方式,但是你卻沒辦法改造它,因為它本身就是第三方的。並且代碼都添加了統計的功能,上面的javascript的廣告鏈接每請求一次都會統計一次,生成的代碼也有點擊統計的功能,也就是說必須以這種方式來進行加載。
document.write是在頁面渲染的時候同步進行的,必須要等javascript代碼下載好並且document.write執行完後才接著渲染後面的內容,如果廣告比較多的話,就會導致頁面阻塞,尤其是在頁面的首屏插好幾個圖片尺寸比較大的這種廣告,那麼阻塞情況就相當明顯和嚴重,會讓用戶覺得你這個網頁很慢。
二、重寫document.write
為了避免阻塞,就不能讓document.write方法在頁面渲染的時候執行,必須想辦法讓javascript的廣告代碼在DOM樹就緒(DOM ready)之後才執行,但是在DOM樹就緒後執行document.write會重新渲染整個頁面,這樣也是不行的。 document.write雖然是瀏覽器原生的方法,但是也可以自定義一個方法來覆蓋掉原來的方法。在javascript廣告代碼加載之前,重寫document.write,等加載並執行完再改回來。
三、延遲加載javascript代碼
上面比較關鍵的一步,延遲加載javascript代碼,如何實現呢?先嘗試通過改寫script的type屬性,比如將type設置成一個自定義的屬性”type/cache”,但這樣大部分瀏覽器(Chrome不會下載)仍然會下載這段代碼,但不會執行,在頁面渲染的時候下載這麼一段代碼仍然會阻塞,通過改寫script的type並不能實現真正的延遲加載,最多能實現只加載不執行,而且還存在兼容問題。
將script標籤放到textarea標籤中,等需要加載的時候再讀取textarea的內容,這樣可以實現真正的延遲加載script,這個方法要感謝玉伯提出的BigRender(牆外)方案。
複製代碼代碼如下:
<div>
<textarea style="display:none">
<script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"></script>
</textarea>
</div>
延遲加載script並重寫document.write,下面是代碼實現:
複製代碼代碼如下:
/**
* 重寫document.write實現無阻塞加載script
* @param { Dom Object } textarea元素
*/
var loadScript = function( elem ){
var url = elem.value.match( /src="([/s/S]*?)"/i )[1],
parent = elem.parentNode,
// 緩存原生的document.write
docWrite = document.write,
// 創建一個新script來加載
script = document.createElement( 'script' ),
head = document.head ||
document.getElementsByTagName( 'head' )[0] ||
document.documentElement;
// 重寫document.write
document.write = function( text ){
parent.innerHTML = text;
};
script.type = 'text/javascript';
script.src = url;
script.onerror =
script.onload =
script.onreadystatechange = function( e ){
e = e || window.event;
if( !script.readyState ||
/loaded|complete/.test(script.readyState) ||
e === 'error'
){
// 恢復原生的document.write
document.write = docWrite;
head.removeChild( script );
// 卸載事件和斷開DOM的引用
// 盡量避免內存洩漏
head =
parent =
elem =
script =
script.onerror =
script.onload =
script.onreadystatechange = null;
}
}
// 加載script
head.insertBefore( script, head.firstChild );
};
四、圖片延遲加載的增強版
實現了無阻塞式的延遲加載javascript廣告代碼,能否進一步優化?如果廣告沒在首屏出現,能否像通常的圖片的延遲加載一樣來進行延遲加載?答案是肯定的。對我之前寫的圖片延遲加載的小插件進行擴展,將原來的圖片加載方式(替換src)改成上面的loadScript方式加載就可以實現。當然,僅僅是這樣的修改還是會有問題的。如果有多個圖片,並且loadScript是同時進行的,而document.write又是全局的方法,保不准在加載A的時候不影響到B,必須讓它們一個個的按順序加載,加載完A之後才能加載B。
五、隊列控制
為了讓javascript廣告代碼按順序加載就需要一個隊列來控制加載。於是又有了下面這段簡單的隊列控制代碼:
複製代碼代碼如下:
var loadQueue = [];
// 入列
var queue = function( data ){
loadQueue.push( data );
if( loadQueue[0] !== 'runing' ){
dequeue();
}
};
// 出列
var dequeue = function(){
var fn = loadQueue.shift();
if( fn === 'runing' ){
fn = loadQueue.shift();
}
if( fn ){
loadQueue.unshift( 'runing' );
fn();
}
};
圖片延遲加載器請參閱比文://www.VeVB.COm/article/50685.htm