重写document.write实现无阻塞加载js广告(补充)
无阻塞加载javascript,对于页面性能优化有很大的作用,这样能有效的减少js对页面加载的阻塞。特别是一些广告js文件,由于广告内容有可能是富媒体,更是很可能成为你页面加载提速的瓶颈,高性能javascript告诉我们,同学,提升你的网页速度,就无阻塞地加载JS吧。
于是便有一下代码出现。
(function(){ vars=document.createElement('script'); s.type='text/javascript'; s.async=true; s.src='http://yourdomain.com/script.js'; varx=document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s,x); })();
上边都是大家熟悉的,看过书的同学都知道这样无阻塞加载的好处,效果挺不错的,当此等无阻塞脚本遇到一般js广告就来了写问题——广告代码出现在HTML里面了却不显示广告。
纳尼?HTML出来了不渲染到页面上?
先看看广告js代码
document.write('<imgsrc="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广告:
functionLoadADScript(url,container,callback){ this.dw=document.write; this.url=url; this.containerObj=(typeofcontainer=='string'?document.getElementById(container):container); this.callback=callback||function(){}; } LoadADScript.prototype={ startLoad:function(){ varscript=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){ varhtml=_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(); } };
页面调用代码:
varloadScript=newLoadADScript('ad.js','msat-adwrap',function(){console.log('msat-adwrap');}); loadScript.startLoad(); varloadScript=newLoadADScript('ad2.js','msat-adwrap',function(){console.log('msat-adwrap2');}); loadScript.startLoad(); varloadScript=newLoadADScript('ad3.js','msat-adwrap',function(){console.log('msat-adwrap3');}); loadScript.startLoad();
广告js代码
//ad.js document.write('<imgsrc="http://images.cnblogs.com/logo_small.gif"alt="Logo">'); //ad2.js document.write('<imgsrc="http://www.baidu.com/img/baidu_sylogo1.gif"width="270"height="129"usemap="#mp">'); //ad3.js document.write('<imgalt="Google"height="95"id="hplogo"src="http://www.google.com/images/srpr/logo3w.png"width="275">');
第一版本的问题是在多个文件调用的时候,会出现一些问题:
1.由于文件加载的速度不一样,导致可能有些先加载有些后加载,也就是无序的,而且很多时候我们需要的是有序的。比如我们需要先加载第一屏的广告。
2.想有些广告需要前置设置一些参数的,例如googleadsense
为了解决这两个问题好进一步修改成最终无阻塞加载js版本。
HTML页面代码:
<!DOCTYPEhtml> <htmllang="en"> <head> <metacharset="utf-8"/> <title>new_file</title> <scripttype="text/javascript"src="loadscript.js"></script> </head> <body> <divid="msat-adwrap"></div> <divid="msat-adwrap2"></div> <scripttype="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({//googleadsense url:'http://pagead2.googlesyndication.com/pagead/show_ads.js', container:'msat-adwrap', init:function(){ google_ad_client="ca-pub-2152294856721899"; /*250x250rich*/ google_ad_slot="3929903770"; google_ad_width=250; google_ad_height=250; }, callback:function(){console.log('msat-adwrap3');} }).execute(); </script> </body> </html>
loadscript.js源代码
/** *无阻塞加载广告 *@authorArain.Yu */ varloadScript=(function(){ varadQueue=[],dw=document.write; //缓存js自身的document.write functionLoadADScript(url,container,init,callback){ this.url=url; this.containerObj=(typeofcontainer=='string'?document.getElementById(container):container); this.init=init|| function(){ }; this.callback=callback|| function(){ }; } LoadADScript.prototype={ startLoad:function(){ varscript=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){ varhtml=_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(newLoadADScript(adObj.url,adObj.container,adObj.init,adObj.callback)); returnthis; }, execute:function(){ if(adQueue.length>0){ adQueue[0].startLoad(); } } }; }());