重写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 联动的无限级封装类,数据采用非Ajax方式,随意添加联动
Jun 29 Javascript
jquery Banner轮播选项卡
Dec 26 Javascript
BootStrap Table后台分页时前台删除最后一页所有数据refresh刷新后无数据问题
Dec 28 Javascript
jQuery ajax请求struts action实现异步刷新
Apr 19 jQuery
使用D3.js制作图表详解
Aug 13 Javascript
Vue动态组件实例解析
Aug 20 Javascript
关于vue单文件中引用路径的处理方法
Jan 08 Javascript
详解如何在react中搭建d3力导向图
Jan 12 Javascript
VeeValidate在vue项目里表单校验应用案例
May 09 Javascript
jQuery实现的点击按钮改变样式功能示例
Jul 21 jQuery
如何使用Javascript中的this关键字
May 28 Javascript
基于JavaScript实现简单的轮播图
Mar 03 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
简单谈谈php延迟静态绑定
2016/01/26 PHP
Mac版PhpStorm之XAMPP整合apache服务器配置的图文教程详解
2016/10/13 PHP
PHP Header用于页面跳转时的几个注意事项
2016/10/21 PHP
PHP排序算法之简单选择排序(Simple Selection Sort)实例分析
2018/04/20 PHP
PHP+MariaDB数据库操作基本技巧备忘总结
2018/05/21 PHP
js中的string.format函数代码
2020/08/11 Javascript
下载文件个别浏览器文件名乱码解决办法
2013/03/19 Javascript
通过JS判断联网类型和连接状态的实现代码
2015/04/01 Javascript
全面解析Bootstrap排版使用方法(文字样式)
2015/11/30 Javascript
理解JavaScript中Promise的使用
2016/01/18 Javascript
基于vuejs实现一个todolist项目
2017/04/11 Javascript
详解Angular 4 表单快速入门
2017/06/05 Javascript
JavaScript基础之流程控制语句的用法
2017/08/31 Javascript
详解node Async/Await 更好的异步编程解决方案
2018/05/10 Javascript
JavaScript 判断对象中是否有某属性的常用方法
2018/06/14 Javascript
基于Vue实现可以拖拽的树形表格实例详解
2018/10/18 Javascript
利用原生JavaScript实现造日历轮子实例代码
2019/05/08 Javascript
详解node登录接口之密码错误限制次数(含代码)
2019/10/25 Javascript
微信小程序开发搜索功能实现(前端+后端+数据库)
2020/03/04 Javascript
python基础教程之面向对象的一些概念
2014/08/29 Python
Python与人工神经网络:使用神经网络识别手写图像介绍
2017/12/19 Python
Python面向对象程序设计示例小结
2019/01/30 Python
python时间序列按频率生成日期的方法
2019/05/14 Python
python实现一个点绕另一个点旋转后的坐标
2019/12/04 Python
详解pycharm2020.1.1专业版安装指南(推荐)
2020/08/07 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
Python偏函数实现原理及应用
2020/11/20 Python
Troy-Bilt官网:草坪割草机、吹雪机、分蘖机等
2019/02/19 全球购物
葛优非诚勿扰搞笑征婚台词
2014/03/17 职场文书
公务员四风问题对照检查材料整改措施
2014/09/26 职场文书
2014乡党委副书记党建工作汇报材料
2014/11/02 职场文书
成绩报告单家长评语
2014/12/30 职场文书
创建文明城市倡议书
2015/04/28 职场文书
公司催款律师函
2015/05/27 职场文书
骆驼祥子读书笔记
2015/06/26 职场文书
《桂花雨》教学反思
2016/02/19 职场文书