Rewrite document.write to load js ads of supplement without blocking

  • 2020-03-30 04:34:21
  • OfStack

Non-blocking loading of javascript has a great effect on page performance optimization, which can effectively reduce the blocking of js on page loading. In particular, some advertising js files, because the advertising content may be rich media, is likely to become your page load speed bottleneck, high-performance javascript told us, students, improve your web speed, load js without blocking.

So here's the code.


(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);
})();

The above are familiar to you, the students who have read the book know the benefits of such non-blocking loading, the effect is pretty good, when the non-blocking script encountered the general js ads to write the problem -- the advertising code appears in the HTML but does not show the ads.

What? HTML comes out without rendering to the page?

First look at the advertising js code


document.write('<img src="http://img.jb51.net/logo_small.gif" alt="Logo">');

The code is very simple, a document.write output HTML code (believe that many advertisers' ads are like this), the page does not show advertising problem? That's the problem with document.write. Why is that? Let's start with w3schools and see how the definition of document.write works.

Definition and usage
The write() method writes HTML expressions or JavaScript code to the document.
List multiple parameters (exp1,exp2,exp3...) , they will be appended to the document in order.

Methods:
One is to use the party to output HTML in the document, and the other is to generate a new document in a window or frame outside the window where the method is called. In the second case, be sure to use the close() method to close the document.

But the idea is to do this during page flow input, and once the page is loaded, call document.write() again, implicitly calling document.open() to erase the current document and start a new one. In other words, if we use document.write after the HTML is loaded, we will generate HTML before we erase it and display the output of document.write.

In our example, the output document.write in HTML will not be executed after the page is loaded. We know the problem, we know the principle, so how do we solve this problem?

Asynchronous use of ajax, lines are different, many advertising files are third-party, under different domain names, there are cross-domain problems, and we can't control the output of their code. In this case, we thought of a way to rewrite document.write, and then rewrite document.write back after the js file is loaded. Look at the code.

The first version of this non-blocking load js ads:


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();
    }
  };

Page calling code:


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();

Advertising js code


//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">');

The problem with the first version is that when multiple files are called, there are some problems:

1. Due to the different speed of file loading, some may be loaded first and some later, which is disordered, and most of the time what we need is orderly. For example, we need to load the first screen of the AD first.

2. Think of some ads that require some parameters in front of them, such as Google adsense

In order to solve these two problems, it can be further modified to finally load the js version without blocking.

HTML page code:


<!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";
      
      google_ad_slot = "3929903770";
      google_ad_width = 250;
      google_ad_height = 250;
    },
    callback:function(){ console.log('msat-adwrap3'); }
  }).execute();
</script>
</body>
</html>

Loadscript. Js source code




var loadScript = ( function() {
  var adQueue = [], dw = document.write;
  //Cache document.write for js itself

  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();
        };
      }
      //Rewrite the document. The 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() {
      //Reduction of the document. The 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();
      }
    }
  };
}());

Related articles: