Non-blocking loading of javascript plays a great role in optimizing page performance. This can effectively reduce the blocking of js on page loading. Especially for some advertising js files, since the advertising content may be rich media, it may become a bottleneck for your page loading speed. High-performance javascript tells us, students, to increase the speed of your web page, load JS without blocking.
Then the following code appears.
(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); })();
Everyone is familiar with the above. Students who have read the book all know the benefits of non-blocking loading. The effect is quite good. When such non-blocking scripts encounter ordinary js advertisements, writing problems arise - the advertisement code appears. In the HTML but no ads are displayed.
Nani? The HTML is not rendered on the page after it comes out?
Look at the advertising js code first
The code is quite simple, just a document.write outputting HTML code (I believe many advertisers’ ads are like this). What’s the problem with the page not displaying ads? The problem lies in this document.write. Why? Let’s first take a look at w3schools to see how the definition of document.write is very useful.
Definition and usage
The write() method writes HTML expressions or JavaScript code to the document.
Multiple parameters (exp1, exp2, exp3,...) can be listed, and they will be appended to the document in order.
Method:
One is to use this method to output HTML in the document, and the other is to generate a new document in a window or frame other than the window in which this method is called. In the second case, be sure to use the close() method to close the document.
But the principle is that it is executed during the page flow input process. Once the page is loaded, calling document.write() again will implicitly call document.open() to erase the current document and start a new document. That is to say, if we use document.write again after the HTML is loaded, the previously generated HTML will be deleted and the content output by document.write will be displayed.
In our example, if document.write is output in HTML after the page is loaded, it will not be executed. Now that we know the problem and the principle, how do we solve this problem?
Using ajax asynchronously, the lines are different. Many advertising files are third-party. Under different domain names, there are cross-domain problems, and we cannot control the output of their codes. 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 loads js ads without blocking:
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, some problems will occur:
1. Due to the different loading speeds of files, some may be loaded first and some may be loaded later, which is disordered, and many times what we need is orderly. For example, we need to load the first screen advertisement first.
2. I think some ads need to set some parameters in advance, such as google adsense
In order to solve these two problems, we further modified it into the final non-blocking loading js version.
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"; /* 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 source code
/** * 无阻塞加载广告 * @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(); } } }; }());