重写document.write实现无阻塞加载js广告(补充)


Posted in Javascript onDecember 12, 2014

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

于是便有一下代码出现。

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

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

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

先看看广告js代码

document.write('<img src="http://img.3water.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广告:

function LoadADScript(url, container, callback){
    this.dw = document.write;
    this.url = url;
    this.containerObj = (typeof container == 'string'?document.getElementById(container):container);
    this.callback = callback || function(){};
  }
  
  LoadADScript.prototype = {
    startLoad: function(){
      var script = document.createElement('script'),
        _this = this;
      
      if(script.readyState){ //IE
        script.onreadystatechange = function(){
        if (script.readyState == "loaded" || script.readyState == "complete"){
          script.onreadystatechange = null;
          _this.finished();
        }
      };
      }else{ //Other
        script.onload = function(){
          _this.finished();
        };
      }
      
      document.write = function(ad){
        var html = _this.containerObj.innerHTML;
        _this.containerObj.innerHTML = html + ad;
      }
      
      script.src = _this.url;
      script.type = 'text/javascript';
      document.getElementsByTagName('head')[0].appendChild(script);
    },
    finished: function(){
      document.write = this.dw;
      this.callback.apply();
    }
  };

页面调用代码:

var loadScript = new LoadADScript('ad.js','msat-adwrap',function(){ console.log('msat-adwrap'); });
  loadScript.startLoad();
  
  var loadScript = new LoadADScript('ad2.js','msat-adwrap',function(){ console.log('msat-adwrap2'); });
  loadScript.startLoad();
  
  var loadScript = new LoadADScript('ad3.js','msat-adwrap',function(){ console.log('msat-adwrap3'); });
  loadScript.startLoad();

广告js代码

//ad.js
document.write('<img src="http://images.cnblogs.com/logo_small.gif" alt="Logo">');

//ad2.js
document.write('<img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" usemap="#mp">');

//ad3.js
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页面代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>new_file</title>
    <script type="text/javascript" src="loadscript.js"></script>
  </head>
<body>
<div id = "msat-adwrap"></div>
<div id = "msat-adwrap2"></div>
<script type="text/javascript">
  loadScript.add({
    url:'ad.js',
    container: 'msat-adwrap',
    callback:function(){ console.log('msat-adwrap'); }
  }).add({
    url:'ad2.js',
    container: 'msat-adwrap2',
    callback:function(){ console.log('msat-adwrap2'); }
  }).add({//google adsense
    url:'http://pagead2.googlesyndication.com/pagead/show_ads.js',
    container: 'msat-adwrap',
    init: function(){
      google_ad_client = "ca-pub-2152294856721899";
      /* 250x250 rich */
      google_ad_slot = "3929903770";
      google_ad_width = 250;
      google_ad_height = 250;
    },
    callback:function(){ console.log('msat-adwrap3'); }
  }).execute();
</script>
</body>
</html>

loadscript.js源代码

/**
 * 无阻塞加载广告
 * @author Arain.Yu
 */

var loadScript = ( function() {
  var adQueue = [], dw = document.write;
  //缓存js自身的document.write

  function LoadADScript(url, container, init, callback) {
    this.url = url;
    this.containerObj = ( typeof container == 'string' ? document.getElementById(container) : container);
    this.init = init ||
    function() {
    };


    this.callback = callback ||
    function() {
    };

  }


  LoadADScript.prototype = {
    startLoad : function() {
      var script = document.createElement('script'), _this = this;

      _this.init.apply();

      if(script.readyState) {//IE
        script.onreadystatechange = function() {
          if(script.readyState == "loaded" || script.readyState == "complete") {
            script.onreadystatechange = null;
            _this.startNext();
          }
        };
      } else {//Other
        script.onload = function() {
          _this.startNext();
        };
      }
      //重写document.write
      document.write = function(ad) {
        var html = _this.containerObj.innerHTML;
        _this.containerObj.innerHTML = html + ad;
      }

      script.src = _this.url;
      script.type = 'text/javascript';
      document.getElementsByTagName('head')[0].appendChild(script);
    },
    finished : function() {
      //还原document.write
      document.write = this.dw;
    },
    startNext : function() {
      adQueue.shift();
      this.callback.apply();
      if(adQueue.length > 0) {
        adQueue[0].startLoad();
      } else {
        this.finished();
      }
    }
  };

  return {
    add : function(adObj) {
      if(!adObj)
        return;

      adQueue.push(new LoadADScript(adObj.url, adObj.container, adObj.init, adObj.callback));
      return this;
    },
    execute : function() {
      if(adQueue.length > 0) {
        adQueue[0].startLoad();
      }
    }
  };
}());
Javascript 相关文章推荐
很酷的javascript loading效果代码
Jun 18 Javascript
JQ获取动态加载的图片大小的正确方法分享
Nov 08 Javascript
js setTimeout()函数介绍及应用以倒计时为例
Dec 12 Javascript
js 跳出页面的frameset框架示例介绍
Dec 23 Javascript
基于jQuery实现响应式圆形图片轮播特效
Nov 25 Javascript
jQuery基于Ajax方式提交表单功能示例
Feb 10 Javascript
解决vue路由后界面没有变化,但是链接有的问题
Sep 01 Javascript
JavaScript Math对象和调试程序的方法分析
May 13 Javascript
layui关闭层级、简单监听的实例
Sep 06 Javascript
layui 上传图片 返回图片地址的方法
Sep 26 Javascript
初学vue出现空格警告的原因及其解决方案
Oct 31 Javascript
webstorm建立vue-cli脚手架的傻瓜式教程
Sep 22 Javascript
js中document.write的那点事
Dec 12 #Javascript
让javascript加载速度倍增的方法(解决JS加载速度慢的问题)
Dec 12 #Javascript
jQuery实现瀑布流布局
Dec 12 #Javascript
jquery+ajax验证不通过也提交表单问题处理
Dec 12 #Javascript
js 左右悬浮对联广告代码示例
Dec 12 #Javascript
原生JavaScript+LESS实现瀑布流
Dec 12 #Javascript
jquery禁止回车触发表单提交
Dec 12 #Javascript
You might like
基于mysql的论坛(3)
2006/10/09 PHP
PHP多线程抓取网页实现代码
2010/07/22 PHP
PHP编码转换
2012/11/05 PHP
thinkphp的c方法使用示例
2014/02/24 PHP
php警告Creating default object from empty value 问题的解决方法
2014/04/02 PHP
php判断类是否存在函数class_exists用法分析
2014/11/14 PHP
ECSHOP在PHP5.5及高版本上报错的解决方法
2015/08/31 PHP
PHP简单实现二维数组的矩阵转置操作示例
2017/11/24 PHP
JavaScript Event学习第三章 早期的事件处理程序
2010/02/07 Javascript
Jquery 一次处理多个ajax请求的代码
2011/09/02 Javascript
jQuery UI Dialog 创建友好的弹出对话框实现代码
2012/04/12 Javascript
javascript中parseInt()函数的定义和用法分析
2014/12/20 Javascript
JS基于Mootools实现的个性菜单效果代码
2015/10/21 Javascript
jquery利用拖拽方式在图片上添加热链接
2015/11/24 Javascript
详解webpack require.ensure与require AMD的区别
2017/12/13 Javascript
Layui Form 自定义验证的实例代码
2019/09/14 Javascript
layui的数据表格+springmvc实现搜索功能的例子
2019/09/28 Javascript
python搭建虚拟环境的步骤详解
2016/09/27 Python
python算法表示概念扫盲教程
2017/04/13 Python
解决python给列表里添加字典时被最后一个覆盖的问题
2019/01/21 Python
Python如何使用内置库matplotlib绘制折线图
2020/02/24 Python
详解Python3 定义一个跨越多行的字符串的多种方法
2020/09/06 Python
css3使用animation属性实现炫酷效果(推荐)
2020/02/04 HTML / CSS
一篇.NET面试题
2014/09/29 面试题
护士实习生自我鉴定范文
2013/12/10 职场文书
单位未婚证明范本
2014/01/18 职场文书
青年安全生产示范岗事迹材料
2014/05/04 职场文书
关于爱国的演讲稿
2014/05/07 职场文书
酒店管理毕业生自荐信
2014/05/25 职场文书
新闻传播专业求职信
2014/07/22 职场文书
大学生国庆节65周年演讲稿范文
2014/09/25 职场文书
2014年绩效考核工作总结
2014/12/11 职场文书
社会治安综合治理责任书
2015/01/29 职场文书
孔庙导游词
2015/02/04 职场文书
公司催款律师函
2015/05/27 职场文书
Python基本的内置数据类型及使用方法
2022/04/13 Python