无阻塞加载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){ //IEscript.onreadystatechange = function(){if (script.readyState == "loaded" || script.readyState == "complete"){script.onreadystatechange = null;_this.finished();}};}else{ //Otherscript.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.jsdocument.write("<img src="http://images.cnblogs.com/logo_small.gif" alt="Logo">");//ad2.jsdocument.write("<img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" usemap="#mp">");//ad3.jsdocument.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 adsenseurl:"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.writefunction 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) {//IEscript.onreadystatechange = function() {if(script.readyState == "loaded" || script.readyState == "complete") {script.onreadystatechange = null;_this.startNext();}};} else {//Otherscript.onload = function() {_this.startNext();};}//重写document.writedocument.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.writedocument.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();}}};}());