I have an application in which the client sends multiple asynchronous javascripts requests to the third party servers. The problem which I am facing is that whenever the client responds to these responses the site becomes inactive for that miliseconds period of time. So sending these multiple requests increases the inactive time. For example if I send x requests and lets assume for each reponse that site becomes inactive for y avg miliseconds and then total inefficient time is x*y. How can I reduce these calls into one call. Third party which I am referring are like calls for google analytics , google ad leads and many more
Here is the example of one of the calls I am making
function () {
var oldonload = window.onload;
window.onload = function(){
__adroll_loaded=true;
var scr = document.createElement("script");
var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" : "http://a.adroll.com");
scr.setAttribute('async', 'true');
scr.type = "text/javascript";
scr.src = host + "/j/roundtrip.js";
((document.getElementsByTagName('head') || [null])[0] ||
document.getElementsByTagName('script')[0].parentNode).appendChild(scr);
if(oldonload){oldonload()}};
}());
First of all: inline async javascript does not block browser. But immediately invoked function does. You don't need to nest window.onload callback into the immediately invoked function.
I recommend you to provide one function doing all things in reaction to browser event. As an example:
window.onload = function() {
//do everything here
}
If it is possible by your app logic put this script right before </body> closing tag.
This might help you. Anyway I also recommend you to measure what your app really doing when script executes. You can do this easily, for example, with chrome developer tools (timeline tab).
The problem might be with how adroll is hijacking the window.onload, which fires after all page rendering is done. If I were you, I'd use jQuery (one of the other things you're calling already likely is, so get it from the Google URL so you only have to download it once). Put it right at the top of your HEAD tag, so that it downloads and loads first, and for YOUR code, use a $(document).ready() call, like this:
<!doctype html>
<html language="en">
<head>
<title>My Webapp</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!--// other library scripts go here -->
... the rest of the HEAD and BODY
<!--// other paste-in tracking code scripts like Google Analytics go here-->
<script>
$(document).ready(new function () {
//Your onload code, which should no longer be blocked
});
</script>
<script type="text/javascript"> //followed by your adroll script
(function () {
var oldonload = window.onload;
window.onload = function(){
__adroll_loaded=true;
var scr = document.createElement("script");
var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" : "http://a.adroll.com");
scr.setAttribute('async', 'true');
scr.type = "text/javascript";
scr.src = host + "/j/roundtrip.js";
((document.getElementsByTagName('head') || [null])[0] ||
document.getElementsByTagName('script')[0].parentNode).appendChild(scr);
if(oldonload){oldonload()}};
}());
</script>
</body>
</html>
This way the scripts will download quietly in the background, and your code can go on and do what it needs to do without them. If it's really the scripts that are blocking your code for running, you can set a timeout function to delay it from loading for half a second or so while your code loads, by changing it like so:
<script type="text/javascript"> //followed by your adroll script
(function () {
var oldonload = window.onload;
window.onload = function(){
setTimeout(function () {
__adroll_loaded=true;
var scr = document.createElement("script");
var host = (("https:" == document.location.protocol) ? "https://s.adroll.com" :
"http://a.adroll.com");
scr.setAttribute('async', 'true');
scr.type = "text/javascript";
scr.src = host + "/j/roundtrip.js";
((document.getElementsByTagName('head') || [null])[0] ||
document.getElementsByTagName('script')[0].parentNode).appendChild(scr);
}, 500);
if(oldonload){oldonload()}
};
}());
</script>
This way, it immediately goes on to do whatever window.onload was supposed to do for your own code, and half a second later, the adroll code will execute asynchronously.
To actually reduce number of js requests you can use bundling on the server. This will allow you to have single request to the server which will grab all necessary js-files and put them in a single response body.
So instead of doing something like this:
<script src='https://s.adroll.com/js/javascrpt1.js' type='text/javascript'></script>
<script src='https://s.adroll.com/js/javascrpt2.js' type='text/javascript'></script>
....
<script src='https://s.adroll.com/js/javascrpt100.js' type='text/javascript'></script>
You can make only one call
<script src='https://yourdomain/getAllJs' type='text/javascript'></script>
You can also implement it in a way that will be more reusable, for example you can pass filenames of the scripts you need on certain page.
<script src='https://yourdomain/getjs?file=javascript1.js&file=javascript2.js&....file=javascript100.js' type='text/javascript'></script>
I'm no sure what server technology you are using, so I didn't put an sample here. In ASP.NET for example you can use Bundling and Minification out of the box.
Related
I have a static script declaration as a requirement, is it possible to have its URL dynamically constructed once browser wants to download it?
<script async src='https://my-known-host.com/script.js?param1=<dynamicValue>'
</script>
This parameter is generated on the client and let's say I want it to be just a random number.
I want to generate random on client side because server caches pages and this request should have a unique random number every time paged is loaded (even from cache)
I need to have it statically declared in order to start the download right away (even before browser parsed to the line of declaration)
Is it possible?
Yeah, a common approach is to write a script that injects the script tag. Something like this:
<script type="text/javascript">
setTimeout(function(){
var a = document.createElement("script");
var b = document.getElementsByTagName("script")[0];
// this adds some number, for example
// you can tweak it to be a random number if you want
a.src = document.location.protocol +
"my-known-host.com/script.js?param1=" +
Math.floor(new Date().getTime()/3600000);
a.async = true;
a.type = "text/javascript";
b.parentNode.insertBefore(a,b)
}, 1);
</script>
I actually took this example script from an analytics provider (crazyegg.com), but Google Analytics does a similar approach. In other words, it's a common thing.
<head>
<script>
$("head").append($("<script>", {async: true, src: "test.js?params=" + Math.random()}));
</script>
<script async src="test.js?params=0.524902342"></script> <!-- parse-able result from the script below -->
</head>
Sorry, I provided jQuery, but this or a vanilla approach should get the job done. The trick is to run JavaScript in head that would append <script src="example.js?" + random></script> to head.
After the browser runs that, it would parse your parameterized <script> at the end of the head tag.
I tested it myself and my browser made a request to test.js?params=0.3607864086033945
Hope that solves it!
EDIT:
If you don't want the script to be at the end of head, you can also do this. Very important that you let the browser parse <script id="randScript" async></script> first.
<script id="randScript" async></script>
<script>
$("#randScript").prop("src", "test.js" + Math.random()); //jQuery
document.getElementById("randScript").src = "test.js" + Math.random(); //vanilla
</script>
I have the following tag which loads a PHP and I need it to be loaded asynchronously thus everything in the containing page is load even if the called PHP file takes longer to return the value (or even is server is down).
<script language="JavaScript"
src="http://www.server.com/phpfile.php">
</script>
I've tried replacing it with the code use by FB and other methods for JS asynchronous loading but it does not work. The issue is that in this case it is a PHP file the one that is called, not a JS file.
I tried the following but it does not work when the called PHP script plugs HTML code (such as a div with an image) with document.write:
<script language="JavaScript">
(function () {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true; s.src = 'server.com/phpfile.php';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
</script>
Any ideas?
Thank you.
The following are the first lines of code in a <script> tag just above the closing body tag in my document (it specifies that a locally-served copy of jQuery is run in the event that Google's CDN fails):
if(!window.jQuery){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/js/jquery.js';
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
jQuery(document).ready(function($){
// page behaviors
});
It does execute successfully, in the sense that if my computer is not connected to the Internet (this is a locally-served page), the local copy of jQuery is inserted. However, the document.ready() section below does not execute. I'm guessing this is because it is invoked before the fallback copy of jQuery takes effect. What's the proper practice for somehow "delaying" its execution so that either copy of jQuery will work properly?
Consider using an existing script loader such as yepnope. There's an example of exactly what you're trying to do on the home page.
You need to be sure that the script you are appending to the dom has finished loading before calling jQuery. You can do this with the technique described here:
if(!window.jQuery){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/js/jquery.js';
script.onreadystatechange= function () {
if (this.readyState == 'complete') jQueryLoaded();
}
script.onload = jQueryLoaded;
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
function jQueryLoaded() { };
You can also fetch the jQuery contents as an Ajax request, create a script tag with those as the body of the script and append it. That would also work.
Try that
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script>
<script>
jQuery(document).ready(function($){
// page behaviors
});
</script>
This way the script tag will be loaded synchronously.
The question "of how do I cope with my CDN failing and load a file hosted on my server" seems to come up a few times lately.
Question I'd ask is whether adding yet more js is the way to achieve the resilience and what level of resilience do the js approaches really add e.g. if the CDN is down they'll be a quick failure but how well do these approaches if the CDN is slow to respond how well do these solutions cope?
An alternative way to approach this is treat it as an infrastructure problem...
Run a CDN based on a domain/sub-domain you own. Have automated monitoring on it's availability, when it fails switch the DNS over to a backup server (anycast may provide an alternative solution too)
A php solution would be something like this:
$google_jquery = 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js';
$fp = #fsockopen($google_jquery, 'r');
if (!$fp)
{
echo '<script type="text/javascript" src="js/jquery.js"></script>';
}
else
{
echo '<script src="'.$google_jquery.'"></script>' }
}
I am loading scripts and style-sheets dynamically from javascript like this.
The problem is that browser does not wait for the script to load.
consider i have a function named functionToBeCalled() inside a script file named script-file.js
i have a function to load script file.
<script type="text/javascript">
var listOfJavaScriptsLoaded = new Array();
function LoadScriptFile(scriptUrl){
var isScriptLoaded = false;
var i = 0;
for(i = 0; i < listOfJavaScriptsLoaded.length; i ++){
if(listOfJavaScriptsLoaded[i] == scriptUrl){
isScriptLoaded = true;
break;
}
}
if(isScriptLoaded == false){
var headTag= document.getElementsByTagName('head')[0];
var scriptTag= document.createElement('script');
scriptTag.type= 'text/javascript';
scriptTag.src= scriptUrl;
headTag.appendChild(scriptTag);
listOfJavaScriptsLoaded.push(scriptUrl);
}
}
LoadScriptFile("script-file.js");
functionToBeCalled();
</script>
now, what happens is that the browser does not wait for the script tag to load and goes to the next command. I get a "undefined functionToBeCalled()" error. this is natural. But the fact is that when i inspect in firebug, the script tag has been formed and the file has loaded.
So how do i make the browser to pause loading and resume after the asset has been loaded?
Edit1: This problem occurs only when i am loading the page in ajax and not in normal page loads
Edit2: Or is there a possibility to read a script/css file from javascript and write it directly in the markup within script tags
If i use window.stop() the loading stops completely. how can i make it resume from the same line?
Or is it possible to make the browser to consider that the loading is still happening and reset it in the onload event?
You may have specific reasons to load the script dynamically, but to present the option, if you write out the script element in your HTML output like so:
<script src="script-file.js"></script>
<script>functionToBeCalled();</script>
the browser will halt parsing until that script has been loaded, and interpreted.
This is also valid in the BODY.
Check out LABjs ( http://labjs.com/ ) by Getify Solutions. LABjs allows script-inserted scripts to be loaded concurrently but run in order.
pretty much every tag which loads a resource has an onload event. so in plain javascript this means in your case something like this:
var s = document.createElement('script');
s.src = "script-file.js";
s.type = 'text/javascript';
head.appendChild(s);
s.onload = function(){
functionToBeCalled();
}
I would recommend looking at Cuzillion. It will allow you to experiment with calling javascript and css in many different ways to see how they react in the browser.
This should answer your question. Just execute it before your page is done loading the body.
<script type="text/javascript">
var loadScriptFile = (function(){
var listOfJavaScriptsLoaded = [];
return function(scriptUrl){
var isScriptLoaded = false;
for(var i = 0; i < listOfJavaScriptsLoaded.length; i ++){
if(listOfJavaScriptsLoaded[i] == scriptUrl){
isScriptLoaded = true;
break;
}
}
if(!isScriptLoaded){
document.write('<scr' + 'ipt type="text/javascript" src="' + scriptUrl + '"></scr' + 'ipt>');
}
};
}());
loadScriptFile("script-file.js");
functionToBeCalled();
</script>
Without using any other JS frameworks (dojo, jquery, etc), how would I dynamically load Google Analytic's javascript to be used on a web page for web-tracking?
The typical appropriate to dynamically loading JS is to do the following:
var gaJs = document.createElement("script");
gaJs.type = "text/javascript";
gaJs.src = "http://www.google-analytics.com/ga.js";
document.body.appendChild(gaJs);
var pageTracker = _gat._getTracker("UA-XXXXXXXXX");
pageTracker._initData();
pageTracker._trackPageview();
But that doesn't work.
The ga.js file isn't loaded in time for _gat._getTracker & _initData/TrackPageview to function.
Any ideas on how to properly dynamically load ga.js.
UPDATE: Seems like someone has attempted to address this problem at the following link. However, it's for use with the old Urchin code and not Google Analytics.
Any ideas on how to get this to work with ga.js instead of urchin.js?
http://20y.hu/20070805/loading-google-analytics-dynamically-on-document-load.html
You could use this snippet from HTML5 Boilerplate.
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
Server side programming would be easier I guess, but I found this some time ago. Notice that it specifically sets it to the html head.
Also check on the first link down on 'Adding Javascript Through Ajax'.
Try using the exact JavaScript code provided by Google and then conditionally display that section of code based on a construct in your UI framework. You didn't say what platform this is running on, if it's ASP.NET you could put the code in a PlaceHolder or UserControl and then set Visible to true or false based on a config file setting if the script should be included. I've used this approach on multiple sites to prevent the Analytics script from being included in pre-production environments.
function loadGA()
{
if(typeof _gat == 'function') //already loaded
{
//innitGA();
// you may want the above line uncommented..
// I'm presuming that if the _gat object is there
// you wouldn't want to.
return;
}
var hostname = 'google-analytics.com';
var protocol = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
js = document.createElement('script');
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', protocol+hostname+'/ga.js');
document.body.appendChild(js);
//2 methods to detect the load of ga.js
//some browsers use both, however
loaded = false; // so use a boolean
js.onreadystatechange = function () {
if (js.readyState == 'loaded')
{
if(!loaded)
{
innitGA();
}
loaded = true;
}
};
js.onload = function ()
{
if(!loaded)
{
innitGA();
}
loaded = true;
};
}
function innitGA()
{
//var pageTracker = _gat._getTracker('GA_ACCOUNT/PROFILE_ID');
//pageTracker._initData();
//pageTracker._trackPageview();
alert('oh hai I can watch plz?');
}
just call loadGA()... tested on IE6/7/8, FF3, Chrome and Opera
sorry if I'm a bit late to this party.
I've literally just put something together that does this... using jquery. The trick is to add a load event to the script tag with the tracking code in it.
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
var gaScript = document.createElement('script');
var loaded = false;
gaScript.src = gaJsHost + "google-analytics.com/ga.js";
$(gaScript).load(function(){
loaded = true;
var pageTracker = _gat._getTracker(Consts.google_analytics_uacct);
pageTracker._initData();
pageTracker._trackPageview();
});
document.body.appendChild(gaScript);
// And to make it work in ie7 & 8
gaInterval = setInterval(function() {
if (!loaded && typeof _gat != 'undefined') {
$(gaScript).load();
clearInterval(gaInterval);
}
},50);
The thing i'm trying to work out is... is this allowed by google.