zoukankan      html  css  js  c++  java
  • 让document.write的广告无阻塞的加载

    广告代码分析

    很多第三方的广告系统都是使用document.write来加载广告,如下面的一个javascript的广告链接。

    1 <script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
    2 ;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"></script>

    这个javascript请求返回的是这样的一段代码:

    1 document.write( "<a href='http://gg.5173.com/adpolestar/wayl/;" +
    2 "ad=6FF3F844_33E6_86EE_3B96_D94C1CF1AEC4;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;" +
    3 "pu=5173;/?http://www.7bao.com/g/xlsbz/index' target='_blank'><img src='" +
    5 "border='0' width="132px" height="58px" /></a>" );

    这种看似有点二的加载方式,但是你却没办法改造它,因为它本身就是第三方的。并且代码都添加了统计的功能,上面的javascript的广告链接每请求一次都会统计一次,生成的代码也有点击统计的功能,也就是说必须以这种方式来进行加载。

    document.write是在页面渲染的时候同步进行的,必须要等javascript代码下载好并且document.write执行完后才接着渲染后面的内容,如果广告比较多的话,就会导致页面阻塞,尤其是在页面的首屏插好几个图片尺寸比较大的这种广告,那么阻塞情况就相当明显和严重,会让用户觉得你这个网页很慢。

    javascript广告的阻塞加载

    重写document.write

    为了避免阻塞,就不能让document.write方法在页面渲染的时候执行,必须想办法让javascript的广告代码在DOM树就绪(DOM ready)之后才执行,但是在DOM树就绪后执行document.write会重新渲染整个页面,这样也是不行的。document.write虽然是浏览器原生的方法,但是也可以自定义一个方法来覆盖掉原来的方法。在javascript广告代码加载之前,重写document.write,等加载并执行完再改回来。

    javascript广告无阻塞加载

    延迟加载javascript代码

    上面比较关键的一步,延迟加载javascript代码,如何实现呢?先尝试通过改写script的type属性,比如将type设置成一个自定义的属性”type/cache”,但这样大部分浏览器(Chrome不会下载)仍然会下载这段代码,但不会执行,在页面渲染的时候下载这么一段代码仍然会阻塞,通过改写script的type并不能实现真正的延迟加载,最多能实现只加载不执行,而且还存在兼容问题。

    将script标签放到textarea标签中,等需要加载的时候再读取textarea的内容,这样可以实现真正的延迟加载script,这个方法要感谢玉伯提出的BigRender(墙外)方案。

    1 <div>
    2 <textarea style="display:none">
    3 <script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
    4 ;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"></script>
    5 </textarea>
    6 </div>

    延迟加载script并重写document.write,下面是代码实现:

    01 /**
    02  * 重写document.write实现无阻塞加载script
    03  * @param { Dom Object } textarea元素
    04  */
    05 var loadScript = function( elem ){
    06     var url = elem.value.match( /src="([sS]*?)"/i )[1],
    07         parent = elem.parentNode,
    08         // 缓存原生的document.write
    09         docWrite = document.write, 
    10         // 创建一个新script来加载
    11         script = document.createElement( 'script' ),
    12         head = document.head ||
    13             document.getElementsByTagName( 'head' )[0] ||
    14             document.documentElement;
    15      
    16     // 重写document.write
    17     document.write = function( text ){
    18         parent.innerHTML = text;
    19     };
    20  
    21     script.type = 'text/javascript';
    22     script.src = url;
    23      
    24     script.onerror =
    25     script.onload =
    26     script.onreadystatechange = function( e ){
    27         e = e || window.event;
    28         if( !script.readyState ||
    29         /loaded|complete/.test(script.readyState) ||
    30         e === 'error'
    31         ){
    32  
    33             // 恢复原生的document.write
    34             document.write = docWrite;
    35             head.removeChild( script );
    36              
    37             // 卸载事件和断开DOM的引用
    38             // 尽量避免内存泄漏
    39             head =         
    40             parent =
    41             elem =
    42             script =
    43             script.onerror =
    44             script.onload =
    45             script.onreadystatechange = null;
    46  
    47         }
    48     }
    49      
    50     // 加载script
    51     head.insertBefore( script, head.firstChild );
    52 };

    图片延迟加载的增强版

    实现了无阻塞式的延迟加载javascript广告代码,能否进一步优化?如果广告没在首屏出现,能否像通常的图片的延迟加载一样来进行延迟加载?答案是肯定的。对我之前写的图片延迟加载的小插件进行扩展,将原来的图片加载方式(替换src)改成上面的loadScript方式加载就可以实现。当然,仅仅是这样的修改还是会有问题的。如果有多个图片,并且loadScript是同时进行的,而document.write又是全局的方法,保不准在加载A的时候不影响到B,必须让它们一个个的按顺序加载,加载完A之后才能加载B。

    队列控制

    为了让javascript广告代码按顺序加载就需要一个队列来控制加载。于是又有了下面这段简单的队列控制代码:

    01 var loadQueue = [];
    02 // 入列
    03 var queue = function( data ){
    04     loadQueue.push( data );
    05     if( loadQueue[0] !== 'runing' ){
    06         dequeue();
    07     }
    08 };
    09 // 出列  
    10 var dequeue = function(){
    11     var fn = loadQueue.shift();
    12     if( fn === 'runing' ){
    13         fn = loadQueue.shift();
    14     }
    15      
    16     if( fn ){
    17         loadQueue.unshift( 'runing' );
    18         fn();
    19     }
    20 };

    图片延迟加载插件的使用说明:http://stylechen.com/imglazyload2.html

    图片延迟加载的增强版插件下载地址:http://stylechen.com/wp-content/uploads/download/imglazyload.zip

    原载于:雨夜带刀's Blog
    本文链接:http://stylechen.com/rewrite-documentwrite.html
  • 相关阅读:
    相对定位和绝对定位
    Html中常见的块级元素
    浮动的用法
    c#中质数判断
    函数
    asp.net Jquery+json 实现无刷新分页
    MS CRM2011 某个用户(团队)对某个实体的操作权限
    asp.net sitemap url动态参数
    MS CRM 2011 更改团队的业务部门
    MS CRM 2011 解决记录总数问题
  • 原文地址:https://www.cnblogs.com/liangxuru/p/5730807.html
Copyright © 2011-2022 走看看