無阻塞載入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輸出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="重寫document.write實作無阻塞載入js廣告(補充)_javascript技巧">'); //ad2.js document.write('<img src="http://www.baidu.com/img/baidu_sylogo1.gif" style="max-width:90%" style="max-width:90%" usemap="#mp" alt="重寫document.write實作無阻塞載入js廣告(補充)_javascript技巧" >'); //ad3.js document.write('<img alt="Google" style="max-width:90%" id="hplogo" src="http://www.google.com/images/srpr/logo3w.png" style="max-width:90%">');
第一版的問題是在多個檔案呼叫的時候,會出現一些問題:
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(); } } }; }());