無阻塞加載javascript,對於頁面性能優化有很大的作用,這樣能有效的減少js對頁面加載的阻塞。特別是一些廣告js文件,由於廣告內容有可能是富媒體,更是很可能成為你頁面加載提速的瓶頸,高性能javascript告訴我們,同學,提升你的網頁速度,就無阻塞地加載JS吧。
於是便有一下代碼出現。
(function() { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'http://yourdomain.com/script.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })();
上邊都是大家熟悉的,看過書的同學都知道這樣無阻塞加載的好處,效果挺不錯的,當此等無阻塞腳本遇到一般js廣告就來了寫問題——廣告代碼出現在HTML裡面了卻不顯示廣告。
納尼?HTML出來了不渲染到頁面上?
先看看廣告js代碼
復制代碼 代碼如下:
document.write('<img src="http://img.jb51.net/logo_small.gif" alt="Logo">');
代碼挺簡單就一個document.write輸出HTML代碼(相信很多廣告商的廣告都這樣),頁面不顯示廣告問題在哪裡呢? 問題就在這個document.write。為什麼?先w3schools看看document.write的定義很使用吧。
定義和用法
write() 方法可向文檔寫入 HTML 表達式或 JavaScript 代碼。
可列出多個參數(exp1,exp2,exp3,...) ,它們將按順序被追加到文檔中。
方法:
一是在使用該方在文檔中輸出 HTML,另一種是在調用該方法的的窗口之外的窗口、框架中產生新文檔。在第二種情況中,請務必使用 close() 方法來關閉文檔。
但其原理是在頁面流輸入過程中執行,一旦頁面加載完畢,再次調用 document.write(),會隱式地調用 document.open() 來擦除當前文檔並開始一個新的文檔。也就是說如果在HTML加載完後我們再使用document.write會檫除之前生成html,而顯示document.write輸出的內容。
而我們例子中在頁面加載完後在在html中輸出document.write,就不會被執行了。問題知道了,原理知道了,那怎麼解決這個問題呢?
異步利用ajax,行不同,很多廣告文件都是第三方的,在不同域名下,存在跨域問題,而且不能我們控制其代碼的輸出。在這種情況下我們想到了一個辦法就是重寫掉document.write,在js文件加載結束後再把document.write重寫回去。看代碼。
第一版本無阻塞加載js廣告:
function LoadADScript(url, container, callback){ this.dw = document.write; this.url = url; this.containerObj = (typeof container == 'string'?document.getElementById(container):container); this.callback = callback || function(){}; } LoadADScript.prototype = { startLoad: function(){ var script = document.createElement('script'), _this = this; if(script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; _this.finished(); } }; }else{ //Other script.onload = function(){ _this.finished(); }; } document.write = function(ad){ var html = _this.containerObj.innerHTML; _this.containerObj.innerHTML = html + ad; } script.src = _this.url; script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script); }, finished: function(){ document.write = this.dw; this.callback.apply(); } };
頁面調用代碼:
var loadScript = new LoadADScript('ad.js','msat-adwrap',function(){ console.log('msat-adwrap'); }); loadScript.startLoad(); var loadScript = new LoadADScript('ad2.js','msat-adwrap',function(){ console.log('msat-adwrap2'); }); loadScript.startLoad(); var loadScript = new LoadADScript('ad3.js','msat-adwrap',function(){ console.log('msat-adwrap3'); }); loadScript.startLoad();
廣告js代碼
//ad.js document.write('<img src="http://images.cnblogs.com/logo_small.gif" alt="Logo">'); //ad2.js document.write('<img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" usemap="#mp">'); //ad3.js document.write('<img alt="Google" height="95" id="hplogo" src="http://www.google.com/images/srpr/logo3w.png" width="275">');
第一版本的問題是在多個文件調用的時候,會出現一些問題:
1. 由於文件加載的速度不一樣,導致可能有些先加載有些後加載,也就是無序的,而且很多時候我們需要的是有序的。比如我們需要先加載第一屏的廣告。
2. 想有些廣告需要前置設置一些參數的,例如google adsense
為了解決這兩個問題好進一步修改成最終無阻塞加載js版本。
HTML頁面代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>new_file</title> <script type="text/javascript" src="loadscript.js"></script> </head> <body> <div id = "msat-adwrap"></div> <div id = "msat-adwrap2"></div> <script type="text/javascript"> loadScript.add({ url:'ad.js', container: 'msat-adwrap', callback:function(){ console.log('msat-adwrap'); } }).add({ url:'ad2.js', container: 'msat-adwrap2', callback:function(){ console.log('msat-adwrap2'); } }).add({//google adsense url:'http://pagead2.googlesyndication.com/pagead/show_ads.js', container: 'msat-adwrap', init: function(){ google_ad_client = "ca-pub-2152294856721899"; /* 250x250 rich */ google_ad_slot = "3929903770"; google_ad_width = 250; google_ad_height = 250; }, callback:function(){ console.log('msat-adwrap3'); } }).execute(); </script> </body> </html>
loadscript.js源代碼
/** * 無阻塞加載廣告 * @author Arain.Yu */ var loadScript = ( function() { var adQueue = [], dw = document.write; //緩存js自身的document.write function LoadADScript(url, container, init, callback) { this.url = url; this.containerObj = ( typeof container == 'string' ? document.getElementById(container) : container); this.init = init || function() { }; this.callback = callback || function() { }; } LoadADScript.prototype = { startLoad : function() { var script = document.createElement('script'), _this = this; _this.init.apply(); if(script.readyState) {//IE script.onreadystatechange = function() { if(script.readyState == "loaded" || script.readyState == "complete") { script.onreadystatechange = null; _this.startNext(); } }; } else {//Other script.onload = function() { _this.startNext(); }; } //重寫document.write document.write = function(ad) { var html = _this.containerObj.innerHTML; _this.containerObj.innerHTML = html + ad; } script.src = _this.url; script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script); }, finished : function() { //還原document.write document.write = this.dw; }, startNext : function() { adQueue.shift(); this.callback.apply(); if(adQueue.length > 0) { adQueue[0].startLoad(); } else { this.finished(); } } }; return { add : function(adObj) { if(!adObj) return; adQueue.push(new LoadADScript(adObj.url, adObj.container, adObj.init, adObj.callback)); return this; }, execute : function() { if(adQueue.length > 0) { adQueue[0].startLoad(); } } }; }());