用js的document.write输出的广告无阻塞加载的方法


Posted in Javascript onJune 05, 2014

一、广告代码分析

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

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

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

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

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

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

用js的document.write输出的广告无阻塞加载的方法

二、重写document.write

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

用js的document.write输出的广告无阻塞加载的方法

三、延迟加载javascript代码

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

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

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

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

/**
 * 重写document.write实现无阻塞加载script
 * @param { Dom Object } textarea元素
 */
var loadScript = function( elem ){
 var url = elem.value.match( /src="([\s\S]*?)"/i )[1],
  parent = elem.parentNode,
  // 缓存原生的document.write
  docWrite = document.write, 
  // 创建一个新script来加载
  script = document.createElement( 'script' ), 
  head = document.head || 
   document.getElementsByTagName( 'head' )[0] || 
   document.documentElement; // 重写document.write
 document.write = function( text ){
  parent.innerHTML = text;
 };

 script.type = 'text/javascript';
 script.src = url;
 script.onerror = 
 script.onload = 
 script.onreadystatechange = function( e ){
  e = e || window.event;
  if( !script.readyState || 
  /loaded|complete/.test(script.readyState) ||
  e === 'error'
  ){
   // 恢复原生的document.write
   document.write = docWrite;
   head.removeChild( script );
   // 卸载事件和断开DOM的引用
   // 尽量避免内存泄漏
   head =    
   parent = 
   elem =
   script = 
   script.onerror = 
   script.onload = 
   script.onreadystatechange = null;
  }
 }
 // 加载script
 head.insertBefore( script, head.firstChild );
};

四、图片延迟加载的增强版

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

五、队列控制

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

var loadQueue = [];
// 入列
var queue = function( data ){
 loadQueue.push( data );
 if( loadQueue[0] !== 'runing' ){
  dequeue();
 }
};
// 出列 
var dequeue = function(){
 var fn = loadQueue.shift();
 if( fn === 'runing' ){
  fn = loadQueue.shift();
 } if( fn ){
  loadQueue.unshift( 'runing' );
  fn();
 }
};

图片延迟加载器请参阅比文:https://3water.com/article/50685.htm 

Javascript 相关文章推荐
Autocomplete Textbox Example javascript实现自动完成成功
Aug 17 Javascript
jQuery库与其他JS库冲突的解决办法
Feb 07 Javascript
jquery ajax 调用失败的原因示例介绍
Sep 27 Javascript
基于jQuery实现带动画效果超炫酷的弹出对话框(附源码下载)
Feb 22 Javascript
AngularJS ng-controller 指令简单实例
Aug 01 Javascript
Js得到radiobuttonlist选中值的两种方法(推荐)
Aug 25 Javascript
jQuery EasyUI 右键菜单--关闭标签/选项卡的简单实例
Oct 10 Javascript
js实现tab切换效果
Feb 16 Javascript
jQuery实现百度登录框的动态切换效果
Apr 21 jQuery
Node.js EventEmmitter事件监听器用法实例分析
Jan 07 Javascript
vue中过滤器filter的讲解
Jan 21 Javascript
解决微信小程序中转换时间格式IOS不兼容的问题
Feb 15 Javascript
javascript数组去重方法终极总结
Jun 05 #Javascript
javascript设计模式之解释器模式详解
Jun 05 #Javascript
javascript监听鼠标滚轮事件浅析
Jun 05 #Javascript
详解JavaScript语法对{}处理的坑爹之处
Jun 05 #Javascript
封装了一个支持匿名函数的Javascript事件监听器
Jun 05 #Javascript
用js读、写、删除Cookie代码分享及详细注释说明
Jun 05 #Javascript
NODE.JS加密模块CRYPTO常用方法介绍
Jun 05 #Javascript
You might like
操作Oracle的php类
2006/10/09 PHP
php防止恶意刷新与刷票的方法
2014/11/21 PHP
PHP中PDO连接数据库中各种DNS设置方法小结
2016/05/13 PHP
php与python实现的线程池多线程爬虫功能示例
2016/10/12 PHP
Thinkphp5 微信公众号token验证不成功的原因及解决方法
2017/11/12 PHP
php实现生成带二维码图片并强制下载功能
2018/02/24 PHP
PHP的PDO大对象(LOBs)
2019/01/27 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
2019/05/29 PHP
php下的原生ajax请求用法实例分析
2020/02/28 PHP
3kb jQuery代码搞定各种树形选择的实现方法
2016/06/10 Javascript
Nodejs抓取html页面内容(推荐)
2016/08/11 NodeJs
微信小程序 Record API详解及实例代码
2016/09/30 Javascript
利用jQuery对无序列表排序的简单方法
2016/10/16 Javascript
AngularJS中的DOM操作用法分析
2016/11/04 Javascript
js 转义字符及URI编码详解
2017/02/28 Javascript
基于vue cli重构多页面脚手架过程详解
2018/01/23 Javascript
vue编译打包本地查看index文件的方法
2018/02/23 Javascript
用vue快速开发app的脚手架工具
2018/06/11 Javascript
微信小程序云开发(数据库)详解
2019/05/17 Javascript
vuex存值与取值的实例
2019/11/06 Javascript
将Python代码打包为jar软件的简单方法
2015/08/04 Python
matplotlib设置legend图例代码示例
2017/12/19 Python
python topN 取最大的N个数或最小的N个数方法
2018/06/04 Python
Python面向对象程序设计中类的定义、实例化、封装及私有变量/方法详解
2019/02/28 Python
python列表每个元素同增同减和列表元素去空格的实例
2019/07/20 Python
pycharm激活码有效到2020年11月底
2020/09/18 Python
Python代码注释规范代码实例解析
2020/08/14 Python
Blue Nile蓝色尼罗河香港官网:世界最大在线钻石珠宝销售商
2020/05/07 全球购物
初任培训自我鉴定
2013/10/07 职场文书
幼儿园教师节演讲稿
2014/09/03 职场文书
公司党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
社区灵活就业证明
2014/11/03 职场文书
党员自我评价2015
2015/03/03 职场文书
2015年工会工作总结
2015/03/30 职场文书
毕业论文致谢信
2015/05/14 职场文书
SQL使用复合索引实现数据库查询的优化
2022/05/25 SQL Server