29
2013
01

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

无阻塞加载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://images.cnblogs.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();
            }
        }
    };
}());



« 上一篇下一篇 »

评论列表:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。