用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 相关文章推荐
图片在浏览器中底部对齐 解决方法之一
Nov 30 Javascript
JavaScript中instanceof运算符的用法总结
Nov 19 Javascript
js闭包的用途详解
Nov 09 Javascript
javascript常用代码段搜集
Dec 04 Javascript
jQuery mobile在页面加载时添加加载中效果 document.ready 和window.onload执行顺序比较
Jul 14 Javascript
JS正则表达式修饰符global(/g)用法分析
Dec 27 Javascript
jQuery Position方法使用和兼容性
Aug 23 jQuery
React 路由懒加载的几种实现方案
Oct 23 Javascript
简单了解vue中父子组件如何相互传递值(基础向)
Jul 12 Javascript
mpvue实现左侧导航与右侧内容的联动
Oct 21 Javascript
Vue触发input选取文件点击事件操作
Aug 07 Javascript
jQuery是用来干什么的 jquery其实就是一个js框架
Feb 04 jQuery
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 file_put_contents()功能函数(集成了fopen、fwrite、fclose)
2011/05/24 PHP
真正根据utf8编码的规律来进行截取字符串的函数(utf8版sub_str )
2012/10/24 PHP
php利用curl抓取新浪微博内容示例
2014/04/27 PHP
PHP中substr函数字符串截取用法分析
2016/01/07 PHP
自制PHP框架之设计模式
2017/05/07 PHP
laravel框架模板之公共模板、继承、包含实现方法分析
2019/08/30 PHP
JQUBAR1.1 jQuery 柱状图插件发布
2010/11/28 Javascript
非常棒的10款jQuery 幻灯片插件
2011/06/14 Javascript
使用jQuery Ajax功能时需要注意的一个问题(内存溢出)
2012/05/30 Javascript
jquery uploadify 在FF下无效的解决办法
2014/09/26 Javascript
jQuery实现自定义右键菜单的树状菜单效果
2015/09/02 Javascript
jquery彩色投票进度条简单实例演示
2020/07/23 Javascript
DIV随滚动条滚动而滚动的实现代码【推荐】
2016/04/12 Javascript
各种选择框jQuery的选中方法(实例讲解)
2017/06/27 jQuery
JavaScript引用类型Object常见用法实例分析
2018/08/08 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
详解微信小程序胶囊按钮返回|首页自定义导航栏功能
2019/06/14 Javascript
JavaScript实现拖拽和缩放效果
2020/08/24 Javascript
python获取指定网页上所有超链接的方法
2015/04/04 Python
python构建深度神经网络(续)
2018/03/10 Python
使用TensorFlow实现简单线性回归模型
2019/07/19 Python
线程安全及Python中的GIL原理分析
2019/10/29 Python
Python Django路径配置实现过程解析
2020/11/05 Python
解决import tensorflow导致jupyter内核死亡的问题
2021/02/06 Python
ALDO英国官网:加拿大女鞋品牌
2018/02/19 全球购物
澳大利亚最大的在线美发和美容零售商之一:My Hair Care & Beauty
2019/08/24 全球购物
大学新生军训个人的自我评价
2013/10/03 职场文书
网站客服岗位职责
2014/04/05 职场文书
争做文明公民倡议书
2014/08/29 职场文书
房产转让协议书(2014版)
2014/09/30 职场文书
小学优秀教师事迹材料
2014/12/16 职场文书
党风廉正建设责任书
2015/01/29 职场文书
认真学习保证书
2015/02/26 职场文书
幼儿园园长个人总结
2015/03/02 职场文书
活动主持人开场白
2015/05/28 职场文书
前端JS获取URL参数的4种方法总结
2022/04/05 Javascript