重写document.write实现无阻塞加载JS广告

开发 前端
无阻塞加载javascript,对于页面性能优化有很大的作用,这样能有效的减少js对页面加载的阻塞。特别是一些广告js文件,由于广告内容有可能是富媒体,更是很可能成为你页面加载提速的瓶颈,高性能javascript告诉我们,同学,提升你的网页速度,就无阻塞地加载JS吧。

无阻塞加载javascript,对于页面性能优化有很大的作用,这样能有效的减少js对页面加载的阻塞。特别是一些广告js文件,由于广告内容有可能是富媒体,更是很可能成为你页面加载提速的瓶颈,高性能javascript告诉我们,同学,提升你的网页速度,就无阻塞地加载JS吧。

于是便有一下代码出现。

  1. (function() {  
  2. var s = document.createElement('script');  
  3. s.type = 'text/javascript';  
  4. s.async = true;  
  5. s.src = 'http://yourdomain.com/script.js';  
  6. var x = document.getElementsByTagName('script')[0];  
  7. x.parentNode.insertBefore(s, x);  
  8. })(); 

上边都是大家熟悉的,看过书的同学都知道这样无阻塞加载的好处,效果挺不错的,当此等无阻塞脚本遇到一般js广告就来了写问题——广告代码出现在HTML里面了却不显示广告。

纳尼?HTML出来了不渲染到页面上?

先看看广告js代码

  1. document.write('<img src="http://images.cnblogs.com/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广告:

  1. function LoadADScript(url, container, callback){  
  2.         this.dw = document.write;  
  3.         this.url = url;  
  4.         this.containerObj = (typeof container == 'string'?document.getElementById(container):container);  
  5.         this.callback = callback || function(){};  
  6.     }  
  7.       
  8.     LoadADScript.prototype = {  
  9.         startLoad: function(){  
  10.             var script = document.createElement('script'),  
  11.                 _this = this;  
  12.               
  13.             if(script.readyState){ //IE  
  14.                 script.onreadystatechange = function(){  
  15.                 if (script.readyState == "loaded" || script.readyState == "complete"){  
  16.                     script.onreadystatechange = null;  
  17.                     _this.finished();  
  18.                 }  
  19.             };  
  20.             }else//Other  
  21.                 script.onload = function(){  
  22.                     _this.finished();  
  23.                 };  
  24.             }  
  25.               
  26.             document.write = function(ad){  
  27.                 var html = _this.containerObj.innerHTML;  
  28.                 _this.containerObj.innerHTML = html + ad;  
  29.             }  
  30.               
  31.             script.src = _this.url;  
  32.             script.type = 'text/javascript';  
  33.             document.getElementsByTagName('head')[0].appendChild(script);  
  34.         },  
  35.         finished: function(){  
  36.             document.write = this.dw;  
  37.             this.callback.apply();  
  38.         }  
  39.     }; 

页面调用代码:

  1. var loadScript = new LoadADScript('ad.js','msat-adwrap',function(){ console.log('msat-adwrap'); });  
  2. loadScript.startLoad();  
  3.       
  4. var loadScript = new LoadADScript('ad2.js','msat-adwrap',function(){ console.log('msat-adwrap2'); });  
  5. loadScript.startLoad();  
  6.       
  7. var loadScript = new LoadADScript('ad3.js','msat-adwrap',function(){ console.log('msat-adwrap3'); });  
  8. loadScript.startLoad(); 

广告JS代码

  1. //ad.js  
  2. document.write('<img src="http://images.cnblogs.com/logo_small.gif" alt="Logo">');  
  3.  
  4. //ad2.js  
  5. document.write('<img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" usemap="#mp">');  
  6.  
  7. //ad3.js  
  8. 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页面代码:

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3.     <head>  
  4.         <meta charset="utf-8" />  
  5.         <title>new_file</title>  
  6.         <script type="text/javascript" src="loadscript.js"></script>  
  7.     </head>  
  8. <body>  
  9. <div id = "msat-adwrap"></div>  
  10. <div id = "msat-adwrap2"></div>  
  11. <script type="text/javascript">  
  12.     loadScript.add({  
  13.         url:'ad.js',  
  14.         container: 'msat-adwrap',  
  15.         callback:function(){ console.log('msat-adwrap'); }  
  16.     }).add({  
  17.         url:'ad2.js',  
  18.         container: 'msat-adwrap2',  
  19.         callback:function(){ console.log('msat-adwrap2'); }  
  20.     }).add({//google adsense  
  21.         url:'http://pagead2.googlesyndication.com/pagead/show_ads.js',  
  22.         container: 'msat-adwrap',  
  23.         init: function(){  
  24.             google_ad_client = "ca-pub-2152294856721899";  
  25.             /* 250x250 rich */ 
  26.             google_ad_slot = "3929903770";  
  27.             google_ad_width = 250;  
  28.             google_ad_height = 250;  
  29.         },  
  30.         callback:function(){ console.log('msat-adwrap3'); }  
  31.     }).execute();  
  32. </script>  
  33. </body>  
  34. </html> 

loadscript.js源代码

  1. /**  
  2.  * 无阻塞加载广告  
  3.  * @author Arain.Yu  
  4.  */ 
  5.  
  6. var loadScript = ( function() {  
  7.     var adQueue = [], dw = document.write;  
  8.     //缓存js自身的document.write  
  9.  
  10.     function LoadADScript(url, container, init, callback) {  
  11.         this.url = url;  
  12.         this.containerObj = ( typeof container == 'string' ? document.getElementById(container) : container);  
  13.         this.init = init ||  
  14.         function() {  
  15.         };  
  16.  
  17.         this.callback = callback ||  
  18.         function() {  
  19.         };  
  20.  
  21.     }  
  22.  
  23.     LoadADScript.prototype = {  
  24.         startLoad : function() {  
  25.             var script = document.createElement('script'), _this = this;  
  26.  
  27.             _this.init.apply();  
  28.  
  29.             if(script.readyState) {//IE  
  30.                 script.onreadystatechange = function() {  
  31.                     if(script.readyState == "loaded" || script.readyState == "complete") {  
  32.                         script.onreadystatechange = null;  
  33.                         _this.startNext();  
  34.                     }  
  35.                 };  
  36.             } else {//Other  
  37.                 script.onload = function() {  
  38.                     _this.startNext();  
  39.                 };  
  40.             }  
  41.             //重写document.write  
  42.             document.write = function(ad) {  
  43.                 var html = _this.containerObj.innerHTML;  
  44.                 _this.containerObj.innerHTML = html + ad;  
  45.             }  
  46.  
  47.             script.src = _this.url;  
  48.             script.type = 'text/javascript';  
  49.             document.getElementsByTagName('head')[0].appendChild(script);  
  50.         },  
  51.         finished : function() {  
  52.             //还原document.write  
  53.             document.write = this.dw;  
  54.         },  
  55.         startNext : function() {  
  56.             adQueue.shift();  
  57.             this.callback.apply();  
  58.             if(adQueue.length > 0) {  
  59.                 adQueue[0].startLoad();  
  60.             } else {  
  61.                 this.finished();  
  62.             }  
  63.         }  
  64.     };  
  65.  
  66.     return {  
  67.         add : function(adObj) {  
  68.             if(!adObj)  
  69.                 return;  
  70.  
  71.             adQueue.push(new LoadADScript(adObj.url, adObj.container, adObj.init, adObj.callback));  
  72.             return this;  
  73.         },  
  74.         execute : function() {  
  75.             if(adQueue.length > 0) {  
  76.                 adQueue[0].startLoad();  
  77.             }  
  78.         }  
  79.     };  
  80. }()); 

原文链接:http://www.cnblogs.com/hongcaomao/archive/2012/03/27/javascript_loadad.html

【编辑推荐】

  1. 5种JavaScript调用函数的方法
  2. 10件有关JavaScript让人费解的事情
  3. 面向对象的JavaScript基本知识指南大全
  4. 是时候开始使用JavaScript严格模式了
  5. 好用的高质量JavaScript库一览
责任编辑:林师授 来源: 红草帽的博客
相关推荐

2014-10-09 09:48:14

JavaScript

2014-11-05 10:31:28

2014-10-10 14:00:52

JavascriptHTML

2009-06-30 15:19:00

阻塞读取远程文件Java多线程

2010-01-07 17:03:31

千兆路由交换机

2010-01-11 09:30:39

千兆路由交换机技术

2021-06-04 18:14:15

阻塞非阻塞tcp

2013-08-22 10:39:03

VOD网络建设VOD网络华为

2015-04-30 12:34:05

WordPressNode.js

2016-12-01 09:24:56

Android

2011-07-28 14:29:45

JavaScript

2017-04-12 10:02:21

Java阻塞队列原理分析

2012-12-28 14:23:12

Android开发TextView

2011-04-25 11:05:10

javascript

2021-09-16 05:32:31

No.js 模块加载器module1.js

2010-04-30 09:45:05

广告木马网络安全卡巴斯基

2009-12-30 15:26:02

Silverlight

2018-04-18 14:38:14

广告

2022-04-21 07:52:08

JS线程GUI渲染

2022-06-22 08:16:29

异步非阻塞框架
点赞
收藏

51CTO技术栈公众号