重写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中的forEach()方法的使用
Jun 08 Javascript
jquery插件EasyUI中form表单提交实例分享
Jan 11 Javascript
jqueryMobile 动态添加元素,展示刷新视图的实现方法
May 28 Javascript
vuejs2.0实现一个简单的分页示例
Feb 22 Javascript
Vue键盘事件用法总结
Apr 18 Javascript
Angularjs修改密码的实例代码
May 26 Javascript
Chrome调试折腾记之JS断点调试技巧
Sep 11 Javascript
vue中接口域名配置为全局变量的实现方法
Sep 20 Javascript
详解如何在Angular优雅编写HTTP请求
Dec 05 Javascript
vue-cli+iview项目打包上线之后图标不显示问题及解决方法
Oct 16 Javascript
JS实现随机抽取三人
Nov 06 Javascript
JS 获取文件后缀,判断文件类型(比如是否为图片格式)
May 09 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
DOTA2 1月28日更新:监管系统降临刀塔世界
2021/01/28 DOTA
php实现购物车功能(上)
2020/07/23 PHP
什么是OneThink oneThink后台添加插件步骤
2016/04/13 PHP
php微信开发自定义菜单
2016/08/27 PHP
ecshop适应在PHP7的修改方法解决报错的实现
2016/11/01 PHP
php readfile下载大文件失败的解决方法
2017/05/22 PHP
深入理解JavaScript系列(8) S.O.L.I.D五大原则之里氏替换原则LSP
2012/01/15 Javascript
时间戳转换为时间 年月日时间的JS函数
2013/08/19 Javascript
javascript正则匹配汉字、数字、字母、下划线
2014/04/10 Javascript
Bootstrap框架的学习教程详解(二)
2016/10/18 Javascript
jquery 实现回车登录详解及实例代码
2016/10/23 Javascript
ES6使用Set数据结构实现数组的交集、并集、差集功能示例
2017/10/31 Javascript
jQuery+Cookie实现切换皮肤功能【附源码下载】
2018/03/25 jQuery
vue使用vue-i18n实现国际化的实现代码
2018/04/08 Javascript
jquery实现选项卡切换代码实例
2019/05/14 jQuery
详解Vue 如何监听Array的变化
2019/06/06 Javascript
深入了解Hybrid App技术的相关知识
2019/07/17 Javascript
JS原型prototype和__proto__用法实例分析
2020/03/14 Javascript
[55:35]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第二场 1月22日
2021/03/11 DOTA
PYTHON正则表达式 re模块使用说明
2011/05/19 Python
Python中关于使用模块的基础知识
2015/05/24 Python
浅谈python类属性的访问、设置和删除方法
2016/07/25 Python
python实现发送邮件及附件功能
2021/03/02 Python
Python判断一个list中是否包含另一个list全部元素的方法分析
2018/12/24 Python
基于h5py的使用及数据封装代码
2019/12/26 Python
迪卡侬英国官网:Decathlon英国
2017/04/08 全球购物
商务专员岗位职责
2013/11/23 职场文书
医药代表个人求职信范本
2013/12/19 职场文书
乡镇干部先进性教育活动个人整改措施
2014/09/16 职场文书
法人代表证明书格式
2014/10/01 职场文书
查摆问题整改措施范文
2014/10/11 职场文书
青年干部培训班学习心得体会
2016/01/06 职场文书
2019优秀干部竞聘演讲稿范文!
2019/07/02 职场文书
广告文案的撰写技巧(实用干货)
2019/08/23 职场文书
纯html+css实现Element loading效果
2021/08/02 HTML / CSS
Python中的socket网络模块介绍
2022/07/23 Python