重写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 相关文章推荐
IE6与IE7中,innerHTML获取param的区别
Mar 15 Javascript
js中更短的 Array 类型转换
Oct 30 Javascript
Jquery异步提交表单代码分享
Mar 26 Javascript
使用JavaScript刷新网页的方法
Jun 04 Javascript
基于javascript实现页面加载loading效果
Sep 15 Javascript
JavaScript ES6的新特性使用新方法定义Class
Jun 28 Javascript
浅谈javascript alert和confirm的美化
Dec 15 Javascript
jQuery自定义元素右键点击事件(实现案例)
Apr 28 jQuery
详解基于mpvue微信小程序下载远程图片到本地解决思路
May 16 Javascript
javascript自定义右键菜单插件
Dec 16 Javascript
JS数组方法join()用法实例分析
Jan 18 Javascript
React倒计时功能实现代码——解耦通用
Sep 18 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 数组遍历的差异(array_diff 的实现)
2008/03/23 PHP
php下使用以下代码连接并测试
2008/04/09 PHP
PHP引用符&amp;的用法详细解析
2013/08/22 PHP
PHP高效获取远程图片尺寸和大小的实现方法
2017/10/20 PHP
thinkphp5.1框架容器与依赖注入实例分析
2019/07/23 PHP
IE autocomplete internet explorer's autocomplete
2007/06/30 Javascript
js event事件的传递与冒泡处理
2009/12/06 Javascript
再论Javascript下字符串连接的性能
2011/03/05 Javascript
深入理解JQuery keyUp和keyDown的区别
2013/12/12 Javascript
js调用打印机打印网页字体总是缩小一号的解决方法
2014/01/24 Javascript
JavaScript将数组转换成CSV格式的方法
2015/03/19 Javascript
JavaScript编写点击查看大图的页面半透明遮罩层效果实例
2016/05/09 Javascript
网页爬虫之cookie自动获取及过期自动更新的实现方法
2018/03/06 Javascript
jQuery中内容过滤器简单用法示例
2018/03/31 jQuery
JavaScript实现打砖块游戏
2020/02/25 Javascript
js实现点击选项置顶动画效果
2020/08/25 Javascript
vite2.0+vue3移动端项目实战详解
2021/03/03 Vue.js
[01:00:30]完美世界DOTA2联赛循环赛 Inki vs Matador BO2第二场 10.31
2020/11/02 DOTA
用python写asp详细讲解
2013/12/16 Python
在Python中使用Mako模版库的简单教程
2015/04/08 Python
彻底搞懂Python字符编码
2018/01/23 Python
Python向MySQL批量插数据的实例讲解
2018/03/31 Python
Pytoch之torchvision.transforms图像变换实例
2019/12/30 Python
15行Python代码实现免费发送手机短信推送消息功能
2020/02/27 Python
深入浅出CSS3 background-clip,background-origin和border-image教程
2011/01/27 HTML / CSS
Osklen官方在线商店:巴西服装品牌
2019/04/25 全球购物
Tomcat中怎么使用log4j输出所有的log
2016/07/07 面试题
静态变量和实例变量的区别
2015/07/07 面试题
Ejb技术面试题
2015/04/29 面试题
《最可爱的人》教学反思
2014/02/14 职场文书
搞笑征婚广告词
2014/03/17 职场文书
摄影专业毕业生求职信
2014/08/05 职场文书
大学生党员个人总结
2015/02/13 职场文书
2015年房产销售工作总结范文
2015/05/22 职场文书
课文《燕子》教学反思
2016/02/17 职场文书
springboot为异步任务规划自定义线程池的实现
2022/06/14 Java/Android