重写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 相关文章推荐
js控制当再次点击按钮时的间隔时间
Jun 03 Javascript
js实现从右向左缓缓浮出网页浮动层广告的方法
May 09 Javascript
jquery显示loading图片直到网页加载完成的方法
Jun 25 Javascript
JS中判断null的方法分析
Nov 21 Javascript
扩展bootstrap的modal模态框-动态添加modal框-弹出多个modal框
Feb 21 Javascript
了解VUE的render函数的使用
Jun 08 Javascript
使用vue-cli+webpack搭建vue开发环境的方法
Dec 22 Javascript
Vue 中axios配置实例详解
Jul 27 Javascript
jQuery 同时获取多个标签的指定内容并储存为数组
Nov 20 jQuery
Vue 组件参数校验与非props特性的方法
Feb 12 Javascript
小程序实现左滑删除的效果的实例代码
Oct 19 Javascript
详解Java中String JSONObject JSONArray List转换
Nov 13 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
饭制《星际争霸》Mod:优化游戏机制 增加新单位
2017/07/02 星际争霸
PHP文本操作类
2006/11/25 PHP
PHP脚本中include文件出错解决方法
2008/11/20 PHP
php.ini中date.timezone设置分析
2011/07/29 PHP
mysql,mysqli,PDO的各自不同介绍
2012/09/19 PHP
php tpl模板引擎定义与使用示例
2019/08/09 PHP
Extjs中常用表单介绍与应用
2010/06/07 Javascript
网站内容禁止复制和粘贴、另存为的js代码
2014/02/26 Javascript
javascript闭包传参和事件的循环绑定示例探讨
2014/04/17 Javascript
javascript生成随机颜色示例代码
2014/05/05 Javascript
jQuery淡入淡出元素让其效果更为生动
2014/09/01 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
2015/08/06 Javascript
js省市县三级联动效果实例
2020/04/15 Javascript
Angular.Js之Scope作用域的学习教程
2017/04/27 Javascript
BootStrap Select清除选中的状态恢复默认状态
2017/06/20 Javascript
教你用Cordova打包Vue项目的方法
2017/10/17 Javascript
VUE2实现事件驱动弹窗示例
2017/10/21 Javascript
聊聊JS动画库 Velocity.js的使用
2018/03/13 Javascript
vue和webpack安装命令详解
2018/06/15 Javascript
详解小程序原生使用ES7 async/await语法
2018/08/06 Javascript
解决vue2 在mounted函数无法获取prop中的变量问题
2018/11/15 Javascript
[02:12]打造更好的电竞完美世界:完美盛典回顾篇
2018/12/19 DOTA
基于Python3 逗号代码 和 字符图网格(详谈)
2017/06/22 Python
Django添加feeds功能的示例
2018/08/07 Python
python实现简单名片管理系统
2018/11/30 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
Python tkinter和exe打包的方法
2020/02/05 Python
Mac PyCharm中的.gitignore 安装设置教程
2020/04/16 Python
如何基于windows实现python定时爬虫
2020/05/01 Python
python使用QQ邮箱实现自动发送邮件
2020/06/22 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
面试必备的求职信
2014/05/25 职场文书
2014年向国旗敬礼活动方案
2014/09/27 职场文书
检讨书格式
2015/01/23 职场文书
平凡的世界读书笔记
2015/06/25 职场文书