用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 相关文章推荐
基于jquery的$.ajax async使用
Oct 19 Javascript
jQuery实现页面滚动时层智能浮动定位实例探讨
Mar 29 Javascript
node.js中的fs.lstatSync方法使用说明
Dec 16 Javascript
JQuery选择器绑定事件及修改内容的方法
Jan 23 Javascript
在JavaScript中操作时间之getUTCDate()方法的使用
Jun 10 Javascript
Angular2 (RC5) 路由与导航详解
Sep 21 Javascript
JS实现鼠标移上去显示图片或微信二维码
Dec 14 Javascript
JSON创建键值对(key是中文或者数字)方式详解
Aug 24 Javascript
jQuery轻量级表单模型验证插件
Oct 15 jQuery
用jQuery将JavaScript对象转换为querystring查询字符串的方法
Nov 12 jQuery
使用 webpack 插件自动生成 vue 路由文件的方法
Aug 20 Javascript
javascript读取本地文件和目录方法详解
Aug 06 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
PHP在Web开发领域的优势
2006/10/09 PHP
PHP中将字符串转化为整数(int) intval() printf() 性能测试
2020/03/20 PHP
web目录下不应该存在多余的程序(安全考虑)
2012/05/09 PHP
PHP实现表单提交时去除斜杠的方法
2016/12/26 PHP
PHP safe_mode开启对于PHP系统函数有什么影响
2020/11/10 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
可恶的ie8提示缺少id未定义
2014/03/20 Javascript
一些老手都不一定知道的JavaScript技巧
2014/05/06 Javascript
BootStrap3中模态对话框的使用
2017/01/06 Javascript
遍历json获得数据的几种方法小结
2017/01/21 Javascript
微信小程序开发之选项卡(窗口底部TabBar)页面切换
2017/04/12 Javascript
NodeJs搭建本地服务器之使用手机访问的实例讲解
2018/05/12 NodeJs
js遍历详解(forEach, map, for, for...in, for...of)
2019/08/28 Javascript
微信小程序scroll-view实现滚动到锚点左侧导航栏点餐功能(点击种类,滚动到锚点)
2020/06/11 Javascript
[06:45]2018DOTA2亚洲邀请赛 4.5 SOLO赛 Sccc vs Maybe
2018/04/06 DOTA
使用PDB简单调试Python程序简明指南
2015/04/25 Python
解决python3在anaconda下安装caffe失败的问题
2017/06/15 Python
详解Python中for循环是如何工作的
2017/06/30 Python
Django 2.0版本的新特性抢先看!
2018/01/05 Python
Python处理文本换行符实例代码
2018/02/03 Python
python实现堆和索引堆的代码示例
2018/03/19 Python
Python 互换字典的键值对实例
2019/02/12 Python
Python学习笔记之读取文件、OS模块、异常处理、with as语法示例
2019/06/04 Python
python实现五子棋小程序
2019/06/18 Python
Numpy ndarray 多维数组对象的使用
2021/02/10 Python
欧缇丽美国官网:Caudalie美国
2016/12/31 全球购物
韩都衣舍天猫官方旗舰店:天猫女装销售总冠军
2017/10/10 全球购物
Bitiba意大利:在线宠物商店
2020/10/31 全球购物
大学生毕业自我鉴定范文
2013/09/19 职场文书
中级会计职业生涯规划书
2014/03/01 职场文书
竞选副班长演讲稿
2014/04/24 职场文书
祖国在我心中演讲稿300字
2014/05/04 职场文书
2014党支部对照检查材料思想汇报
2014/10/05 职场文书
redis 查看所有的key方式
2021/05/07 Redis
Python Pycharm虚拟下百度飞浆PaddleX安装报错问题及处理方法(亲测100%有效)
2021/05/24 Python
零基础学java之带返回值的方法的定义和调用
2022/04/10 Java/Android