I have two scripts that load in my head area which are:
<script type="text/javascript">
var myDate = new Date();
var myStamp = ""+myDate.getDate()+myDate.getMonth()+myDate.getFullYear()+myDate.getHours()+myDate.getMinutes();
document.write('<script type="text/javascript" src="https://scriptlocation.com/js/script.js?' + myStamp + '"><\/script>');
</script>
<script type="text/javascript">
document.write('<script type="text/javascript" src=""https://scriptlocation.com/js/script2.js?'+SVNInfo.revision+'"><\/script>');
</script>
My ideal scenario would be to merge these two into 1 javascript file, so I've tried to create a new file which contains the following:
var myDate = new Date();
var myStamp = ""+myDate.getDate()+myDate.getMonth()+myDate.getFullYear()+myDate.getHours()+myDate.getMinutes();
document.write('<script type="text/javascript" src="https://scriptlocation.com/js/script.js?' + myStamp + '"><\/script>');
document.write('<script type="text/javascript" src=""https://scriptlocation.com/js/script2.js?'+SVNInfo.revision+'"><\/script>');
however that does not seem to work as the script is not being loaded properly. Do I need to rewrite / change something to have this working correctly?
Some advice would be greatly appreciated.
This should do the trick.
<script type="text/javascript">
var myStamp = (new Date()).getTime(), //easier way to get a relatively unique timestamp
script1 = document.createElement('script'),
script2 = document.createElement('script'),
body = document.getElementsByTagName('body')[0];
script1.setAttribute('type', 'text/javascript');
script1.setAttribute('src', 'https://scriptlocation.com/js/script.js?' + myStamp);
script2.setAttribute('type', 'text/javascript');
script2.setAttribute('src', 'https://scriptlocation.com/js/script2.js?' + SVNInfo.revision); //is SVNInfo.revision defined?
body.appendChild(script1);
body.appendChild(script2);
</script>
For the record, document.write is an extremely primitive (and not a friendly way) of adding scripts to the page. Just create them as elements and then add them to the DOM.
SVNInfo is defined by the first script. So you can't include the second script (whose source path includes the value of SVNInfo) until you've executed the first.
When you document.write content from a <script> block, it's adding new content for the HTML parser at the point just after the </script> end-tag of the current block. If that content itself includes a <script> element, then the script inside it cannot start to execute until the current script has finished. So the </script><script> tags break up the current script execution allowing the script inserted by document.write to run. You won't be able to put both writes into the same script without breaking this.
You may be able to do it the way pete suggests with inserting script elements directly into the DOM, avoiding the parser completely. Because no parser is involved, the inserted script can execute straight away, without waiting for the current script block to complete.
However, you will need to ensure that the first script element is added to the document and run before you attempt to set the src of the second script element, because you need SVNInfo to be ready. Also, you should append the scripts to the <head> element and not <body> because writing to an element that hasn't been completely parsed yet is likely to cause the page load to abort in IE.
It is also possible that this might still not work, if there's something in the second script that expects to be running from a document.write-invoked script. Can't really see from here as the script is unreadable compressed.
Given that the integration doc tells you what it wants you to do, I'd stick with that; even if you have a better way, they might change the script at some point in the future in a way that relies on the two document.write scripts.
Related
I have some javascript that is not required for my initial page load. I need to load it based on some condition that will be evaluated client-side.
$(document).ready(function() {
let someCondition = true; // someCondition is dynamic
if (someCondition) {
var element = document.createElement('script');
element.src = 'https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js';
element.defer = true; // does this make a difference?
element.onload = function() {
// do some library dependent stuff here
document.getElementById("loading").textContent = "Loaded";
};
document.body.appendChild(element);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="loading">Loading...</h1>
Does it make a difference (in terms of how browser will treat the script tag), if a new tag created using javascript, after document is ready, has 'defer' attribute or not? I think there is no difference, but how can I say for sure?
I believe I understand how deferred scripts behave when script tag is part of the initial html (as described here). Also, this question is not about whether element.defer=true can be used or not (subject of this question).
No that doesn't make any difference, the defer attribute is ignored in case of "non-parser-inserted" scripts:
<script defer src="data:text/javascript,console.log('inline defer')"></script>
<script>
const script = document.createElement("script");
script.src = "data:text/javascript,console.log('dynamic defer')";
script.defer = true;
document.body.append(script);
</script>
<!-- force delaying of parsing -->
<script src="https://deelay.me/5000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Look at your browser's console or pay attention to the logs timestamps to see that the dynamically inserted script actually did execute while we were waiting for the delayed script to be fetched.
There's a difference between adding them to the function and adding directly the CDN ( especially in your case ).
Let's look at the code execution of the above-mentioned code first,
You have added the jquery CDN first ( without defer ) so that loads first.
$(document).ready will be fired once after the complete load of jquery.
There'll be the creation and insertion of a new script tag to the dom.
Download the https://raw.githubusercontent.com/Useless-Garbage-Institute/useless-garbage/master/index.js asynchronously.
Let's look at another approach: adding CDN to the code:
Your DOM will have 2 script tags.
Both will start loading based on the type of load parallelly ( defer async etc ).
Notice you are not waiting for the dom ready event to load the second script.
I suggest adding only the main JS part in a js file and adding it to the CDN. Others can wait load with the delay.
In case you are really needed with a js src, then don't load it the first way since it waits for the complete page load.
I suggest you read and look at web-vitals and SEO for this.
and for your other question, yes you can add defer attribute with element.defer=true to the elements while creating and loading to DOM.
Hope this answer helps you!
Feel free to comment if you get any errors or doubts.
I think the JQuery Arrive lib will solve your case.
I am on an e-commerce platform where I can edit the <head>, however some things that are injected into the head are out of reach for users. So even though we can edit the <head>, there are injections which are out of reach and therefore unremovable via the traditional method.
PS: I can put script before or after these injected JS script tags, which are generated and populated along with my scripts. And so my script would run before the injected tags if I place my script before their "tag injection line."
The Problem
The problem is, this platform started injecting analytics and spam into the head, basically jacking our customers info and selling it to third parties. So I want to disable their crappy scripts.
<script type="text/javascript" async="" src="/some.JS.file.min.js"></script>
<script type="text/javascript" async="" src="/another.JS.file.min.js"></script>
The Question
Is it possible with javascript or jquery to write a script that will edit tags before they run? I can insert this custom script before the tags are in injected. I was wrong -- the unwanted <script> tags are always PREpended to the first non-commented <script> tag, and so no javascript will work to hack up the tags before they run.
What I Have Tried So Far
I found this incomplete and not working answer from this SO question.
When I run the full script with the right details entered for my own site, I get so many errors it's difficult to know where to begin as I have no idea what all the XHR stuff is for or what it does, and some of the errors are ones I've never even seen before.
When I run just this part, which I somewhat understand:
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
if(script.getAttribute("src") == "/some.JS.file.min.js"
|| script.getAttribute("src") == "/another.JS.file.min.js") {
script.removeAttribute("src");
}
});
EDIT UPDATE:
Their script is inserted AFTER my scripts. That is, I can insert the script into the <head> before their script tags or after. We are looking into new platforms now but I still need to solve this in the meantime as it will be months before we switch. I was hoping g there is some JavaScript I am not aware of that can edit HTML script tags before they run, if this script runs before they do.
EDIT 2:
Nit's answer window.bcanalytics = function () {}; works great and breaks most of it by breaking window.bcanalytics.push but somehow some of it still survives.
In this block:
<script type="text/javascript">
(function() {
window.bcanalytics || (window.bcanalytics = []), window.bcanalytics.methods = ["debug", "identify", "track",
"trackLink", "trackForm", "trackClick", "trackSubmit", "page", "pageview", "ab", "alias", "ready", "group",
"on", "once", "off", "initialize"], window.bcanalytics.factory = function(a) {
return function()
{
var b = Array.prototype.slice.call(arguments);
return b.unshift(a), window.bcanalytics.push(b),
window.bcanalytics
}
};
for (var i = 0; i < window.bcanalytics.methods.length; i++)
{
var method = window.bcanalytics.methods[i];
window.bcanalytics[method] = window.bcanalytics.factory(method)
}
window.bcanalytics.load = function() {
var a = document.createElement("script");
a.type = "text/javascript",
a.async = !0, a.src = "http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/app/assets/js/vendor/bigcommerce/analytics.min.js";
var b = document.getElementsByTagName("script")[0];
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
b.parentNode.insertBefore(a, b)
// ^^^ This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
}, window.bcanalytics.SNIPPET_VERSION = "2.0.8", window.bcanalytics.load();
bcanalytics.initialize({"Fornax": {"host": "https:\/\/analytics.bigcommerce.com","cdn": "http:\/\/cdn5.bigcommerce.com\/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707\/app\/assets\/js\/vendor\/bigcommerce\/fornax.min.js","defaultEventProperties": {"storeId": 729188,"experiments": {"shipping.eldorado.ng-shipment.recharge-postage": "on","shipping.eldorado.label_method": "on","cp2.lightsaber": "on","PMO-272.cp1_new_product_options": "on","cart.limit_number_of_unique_items": "control","cart.auto_remove_items_over_limit": "control","BIG-15465.limit_flash_messages": "control","BIG-15230.sunset_design_mode": "control","bigpay.checkout_authorizenet.live": "on","bigpay.checkout_authorizenet.live.employee.store": "control","bigpay.checkout_authorizenet.test": "on","bigpay.checkout_authorizenet.test.employee.store": "control","bigpay.checkout_stripe.live": "on","bigpay.checkout_stripe.live.employee.store": "control","bigpay.checkout_stripe.test": "on","bigpay.checkout_stripe.test.employee.store": "control","sessions.flexible_storage": "on","PMO-439.ng_payments.phase1": "control","PMO-515.ng_payments.phase2": "control","PROJECT-331.pos_manager": "control","PROJECT-453.enterprise_apps": "control","shopping.checkout.cart_to_paid": "legacy_ui","onboarding.initial_user_flow.autoprovision": "on","faceted_search.enabled": "off","faceted_search.displayed": "off","themes.previewer": "enabled"}},"defaultContext": {"source": "Bigcommerce Storefront"},"anonymousId": "24a35a36-7153-447e-b784-c3203670f644"}});
})();
</script>
window.bcanalytics.load manages to survive and loads analytics.min.js (according to the Network tab), though I can't tell if the script then runs or doesn't.
Also, I've figured out that these pesky HTML lines:
<script type="text/javascript" defer="" async="" src="http://tracker.boostable.com/boost.bigcommerce.js"></script>
<script type="text/javascript" async="" defer="" src="http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/javascript/jirafe/beacon_api.js"></script>
<script type="text/javascript" async="" src="http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/app/assets/js/vendor/bigcommerce/analytics.min.js"></script>
<script type="text/javascript" async="" src="http://www.google-analytics.com/plugins/ua/ecommerce.js"></script>
are Always PREpended to the first non-commented <script> opening tag, so unfortunately, none of the creatively destructive methods below will work, as any script I try to insert ahead of these tags will automatically find the pesky unwanted lines appended before it.
Assuming the offending code is similar to that of the question you linked to, I would simply try to break the offending code so it fails to execute.
From hereon the answer relies on code from the other question since you didn't provide any.
The offending code relies on analytics, which is ensured on the page at the beginning of the script:
(function(){
window.analytics||(window.analytics=[]),window.analytics.methods=["debug","identify","track","trackLink","trackForm","trackClick","trackSubmit","page","pageview","ab","alias","ready","group","on","once","off","initialize"],window.analytics.factory=function(a){return function(){var b=Array.prototype.slice.call(arguments);return b.unshift(a),window.analytics.push(b),window.analytics}};for(var i=0;i<window.analytics.methods.length;i++){var method=window.analytics.methods[i];window.analytics[method]=window.analytics.factory(method)}window.analytics.load=function(){var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/analytics.min.js";var b=document.getElementsByTagName("script")[0];b.parentNode.insertBefore(a,b)},window.analytics.SNIPPET_VERSION="2.0.8",window.analytics.load();
//The rest of the script
})();
To break the whole script and prevent it from running you should simply assign window.analytics a value that will conflict with the methods that are used.
So, for example, you could run a script before the offending script that simply assigns the following:
window.analytics = function () {};
Which will result in the offending script failing due to a type error.
If you know you can at least get your scripts to run first, one (albeit hacky) solution is to just absolutely "trash" the JS environment for the next script, so it has some problems. For example:
//trash it
document.getElementById=null;
document.querySelector=null;
document.querySelectorAll=null;
window.console=null;
window.alert=null;
document.getElementsByTagName=null;
document.getElementsByClassName=null;
As soon as the enemy script tries using one of those functions, it will just crap out. Those are just some common methods off the top of my head... find out which ones its using, and nuke those. Of course, nuking anything you need for events on your own page could be an issue.
How are the scripts being injected? If it's through something like document.createElement, you could attempt to hijack that function and disable it if the element name is script:
var origCreate = document.createElement;
document.createElement = function (name) {
if (name.toLowerCase() !== 'script') {
origCreate.call(document, name);
}
};
Since the scripts are being inserted server-side, you won't be able to disable the running of the scripts in your JavaScript. However, if you're able to inject any arbitrary text before and after the scripts being inserted, you could try commenting out the script tags by inserting this first:
<!--
...then this after:
-->
If the scripts get injected between these, it will hopefully cause the HTML parser to ignore the scripts.
Update:
Sounds like you need to disable just some of this content, so commenting everything out won't work. However, if before/after hijacking works, you could potentially wrap the injected scripts in a DOM element, parse that content, strip out the scripts you don't want, and inject the scripts so they run:
Inject something like this before:
<style id="hijack" type="text/html">
...and this after:
</style>
<script>
var hijackedWrapper = document.getElementById('hijack');
var scripts = hijackedWrapper.textContent;
scripts = scripts.replace('<script src="http://some.domain.com/foo.js"></s' + 'cript>', '');
document.write(scripts); // There's better ways to do this, but is just an illustration
</script>
Like the others, I would suggest sabotaging the js environment for the hostile script, and then recovering it back once you need it.
For example, if the script relies on document.getElementById, you can do this
var restore = {
getElementById: document.getElementById
};
document.getElementById = null;
and then if you have a need to use document.getElementById later, you can restore it back:
document.getElementById = restore.getElementById;
I also wanted to note that removing the actual script tags, as far as I can tell, is not possible:
If you put in a script before the hostile scripts, then they will not be loaded in the DOM yet, so it can't see anything to remove.
If you put in a script after the hostile scripts, the hostile scripts will already be loaded.
Is there a cross-browser way to associate an onload event to a static script tag, in a html document?
The following will not work in IE 7 and IE 8:
<script onload="DoThat" type="text/javascript" src="..."></script>
Some background
I have found a way to accomplish this with a dynamic script tag and if statements. It is for example explained in this MSDN article.
My issue is that I need to locate the current script tag, because I am building widgets that insert DOM elements in-place. In the past I have found some workarounds to do this, but they all have their downside. I am hoping that using the "this" keyword on a script onload event will help.
As far as I understand, you need your code to know what script element it belongs to. You would then insert necessary HTML widget content right next to this script or append to the script parent, etc. If so then I would propose you another approach.
Inside you widget javascript file you place next line of code:
var scripts = document.getElementsByTagName('script'),
thisScript = scripts[scripts.length - 1];
thisScript is the DOM element you are looking for, current script element this code located in. So simple.
Why it works.
Detail explanation http://feather.elektrum.org/book/src.html. Just an abstract:
When a script with a src is loaded in a page, the rest of the page is not yet written. The implication is that no matter how many scripts a page contains, the one currently starting to execute is the last one
This is fairly simple and effective technic, although but not so many people know about this possibility. It's reliable and 100% browser compatible. By the way, Google uses this code to render +1 buttons:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{lang: 'dk'}
</script>
So how do think how the api script could get this hash parameters? In my example it would be thisScript.innerHTML
I'm not sure if I caught your needs.
Anyway to locate the current script you can try this example
<script src="jquery-1.7.1.min.js" varTest="valueTest" id="myID"></script>
<script>
var scripts = document.getElementsByTagName('script');
var pattern = 'jquery-1.7.1.min.js';
var i = undefined;
for (var x=0; x<scripts.length; x++)
{
if (scripts[x].src.match(pattern)) { i = x; break; }
}
if (typeof(i) != 'undefined')
{
// Do any association needed here
var current_script = scripts[i];
// Just for test
console.log(current_script.attributes);
}
</script>
This is a bit of an oddball use case, but I have my reasons:
I'd like to be able to write
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
in my markup and, using the code in first.js, prevent or delay the execution of second.js. Is this possible, in any browser? What if the contents of first.js are inlined? (If it helps, assume that the second script tag has an id attribute.)
Since I've gotten a couple of answers that missed what I'm getting at, I should clarify:
The solution must be entirely within first.js. Anything that require changes to the original HTML of the page, or to second.js, is not acceptable.
It is acceptable to load second.js via Ajax and execute it using eval. That's the easy part. The hard part is preventing the immediate execution of second.js.
Assume that you don't know what's in second.js. So, you can't just replace each global function called by second.js with a no-op function. (Plus, this would almost certainly lead to errors.)
If you know of a solution that works in some browsers but not in others, I'd love to hear it.
Example: To make this a little more concrete, let's say that the code
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
setTimeout(func, 10);
</script>
precedes the two script includes, and that second.js contains the line
if (window.meaningOfLife !== 42) {throw new Error();}
first.js should be able to prevent this error by delaying second.js from executing until window.loadSecond is run. (Assume the implementation of window.loadSecond is also in first.js.) It is not allowed to touch window.meaningOfLife.
Update: Alohci's answer meets these requirements, but only on the condition that the second script tag comes immediately after the first, with nothing but whitespace in between. If someone could extend his hack to avoid that requirement, without introducing other unwanted consequences, that would be amazing...
Given your specific requirements set, this is actually quite simple and should work completely cross-browser. It does require however, that first.js immediately precedes second.js without anything between them except white space.
First, let's assume that the HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Test Case</title>
<meta charset="UTF-8" />
<script type="text/javascript">
function func() {
window.meaningOfLife = 42;
window.loadSecond();
};
</script>
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
</head>
<body>
<p>Lorem ipsum dolor sit amet ...</p>
Run Func()
</body>
</html>
I've removed the setTimeout because that can cause func() to run before start.js runs causing a "loadSecond is not defined" error. Instead, I've provided an anchor to be clicked on to run func().
Second, let's assume that second.js looks like this:
document.body.appendChild(document.createTextNode("second.js has run. "));
if (window.meaningOfLife !== 42) {throw new Error();}
Here, I've just added a line to append some text to the document body, so that it is easier to see when second.js actually runs.
Then the solution for first.js is this:
function loadSecond()
{
var runSecond = document.createElement("script");
runSecond.setAttribute("src", "second.js");
document.body.appendChild(runSecond);
}
document.write("<script type='application/x-suppress'>");
The loadSecond function is just there to run second.js when func() runs.
The key to the solution is the document.write line. It will inject into the HTML <script type='application/x-suppress'> between the close script tag of first.js and the open script tag of second.js.
The parser will see this and start a new script element. Because the type attribute has a value which is not one that the browser recognises as being JavaScript, it will not attempt to run its content. (So there are an infinite number of possible type attribute values you could use here, but you must include a type attribute, as in its absence, the browser will assume that the script's content is JavaScript.)
The second.js script's opening tag will then be parsed as text content of the new script element and not executed. Finally the second.js script's closing tag will be re-purposed to close the new script element instead, which means that the remainder of the HTML is parsed correctly.
You can see a working version at http://www.alohci.net/static/jsprevent/jsprevent.htm
In first.js, set var shouldILoad = true
Then, load second.js this way:
<script>
if (shouldILoad) {
(function() {
var myscript = document.createElement('script');
myscript.type = 'text/javascript';
myscript.src = ('second.js');
var s = document.getElementById('myscript');
s.parentNode.insertBefore(myscript, s);
})();
}
</script>
(where 'myscript' is the ID of some element before which you'd like to insert the new Script element)
As far as I know, you can't. If the markup looks like
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
you can't access the second script element from within first.js, as it hasn't been added to the DOM at the moment the first script runs (even not if you assign an id to the second element). It doesn't matter whether the code of second.js is put inline or in an external file.
The only thing I don't understand is your second point. First you say that you can't control the markup of the document, but then you state it is possible to load second.js dynamically (using AJAX).
Following article describes the way you could block (3-rd party) scripts loading/execution from your script (including the both tag in the page head and dynamically added tags).
To handle existing tags on a page:
Use a MutationObserver to observe script elements insertion and inside the MutationObserver callback backup the script (to enable/insert it later) and change the script type to "javascript/blocked" (not works in IE, Edge, Firefox). Also you could handle deprecated (but working) beforescriptexecute event in Firefox to prevent script load.
Manually set type "javascript/blocked" (works everywhere including IE and Edge) like
<script type="text/javascript" type="javascript/blocked" src="second.js"></script>, then backup it in MutationObserver callback and re-add it later.
To handle dynamically added tags
Monkey-patch the document.createElement.
Override ‘src’ and ‘type’ descriptors on the HTMLScriptElement prototype.
Also this guys provide a yett library with the approach described in the article.
All <script> tags have their own execution context, which makes it nearly impossible to interfere with each other. Of course you've got the (infamous) global object (referenced by window in browsers).
Preventing the execution of second.js is rather simple: break it!
Assuming that second.js tries to call document.getElementById for example:
Working example of breaking jQuery, then loading later (with dependecies).
Tested on: IE 6+, FF 3.6+, Chrome
end of first.js
var execute;
// saving our position
var scripts = document.getElementsByTagName("script");
var i = scripts.length;
// breaking getElementById
var byId = document.getElementById;
document.getElementById = null;
var interval = setInterval(function () {
if (i != scripts.length) {
var second = scripts[i];
// stop polling
clearInterval(interval);
// fix getElementById
document.getElementById = byId;
// set the delayed callback
execute = function (onload) {
var script = document.createElement("script");
script.src = second.src;
script.onload = script.onreadystatechange = onload;
document.getElementsByTagName("head")[0].appendChild(script);
};
}
}, 100);
anytime you wanna execute second.js
execute(function(){
// second.js dependant code goes here...
});
Note: the onload parameter for execute is optional.
Just to see if this was possible, I had first.js send a synchronous XHR to a PHP file, and had the PHP file delete second.js. When the readyState reached '4', I had the JS alert something, to stop the thread. Then I went and checked the server... Yeah, second.js was deleted. And yet, it wouldn't work. I'd close the alert box, and the code that was in second.js would still be executed, despite the fact that the file was gone.
I don't really know what this means, but the answer to your question is probably, "No, it's not possible."
you may use setTimeout() to delay the execution of some code
This is more a curiosity question than anything else...
Google instructs to add the analytics tracking code as follows:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try{
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
pageTracker._trackPageview();
} catch(err) {}
</script>
I'm wondering some JS guru here could tell me why they're separating it into two script tags instead of sticking it all inside one. I know that the top part could be put in the header and the bottom part just before body tag to ensure the page loaded before it's tracked, but I'm wondering if there's something more to it. Anyone who'd know that would likely know how to separate the code into two tags anyway.
I'm only asking as this is coming from the Goog and is being used by millions of sites...
Thanks
This is to be consistent cross-browser, it needs to make sure that document.write() sticks the tag it's generating in before the next script block runs, so the result looks like this:
<script src='http://www.google-analytics.com/ga.js' type='text/javascript'></script>
<script type="text/javascript">
try{
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
pageTracker._trackPageview();
} catch(err) {}
</script>
If it didn't do it in 2 scripts, _gat would be undefined in the second script block because the ga.js hasn't loaded yet...however as a script block before that one, the page waits before executing the code inside the last block, making it all work when it's supposed to. Basically, the browser executes script blocks in order, google's taking advantage of this fact to load the script when it needs to be loaded...which is before it's used.
If I had to guess, I would say it's because of the document.write(unescape(...)); line where they're dynamically including another JS file. Why they're loading that one dynamically I don't know. I'd guess that has to do with whether or not you're running https or http.
If it was one file, I doubt the ga.js file would be loaded in time to run the pageTracker code. _gat wouldn't exist, and you'd get an error rather than being able to actually use Analytics.