MyBB Internal: One or more warnings occured. Please contact your administrator for assistance.
WebPagetest Forums - Inline JavaScript Experiment

WebPagetest Forums

Full Version: Inline JavaScript Experiment
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2 3
Hey Guys,

I would say most websites have a bit of JavaScript in their header section. I setup an experiment to see which of the following exhibits would process faster:

Exhibit A

<script type="text/javascript" src=""></script>

Exhibit B

<script type="text/javascript">
// <![CDATA[
var bodyHasLoaded=0; var swfObjectHasLoaded=0; var swfObjectIsLoading=0; var javascriptReady=false; var protoaculousHasLoaded=0; var myDirectory="http://"+document.domain; var myDirectory2=""; function updateBody() { } function addLoadEvent(func) { if(bodyHasLoaded==1) { func(); } else { var oldonload=window.onload; if (typeof window.onload!='function') { window.onload=func; } else { window.onload=function() { if(oldonload) { oldonload(); } func(); }; } } } function firstOnBodyLoadedEvent() { bodyHasLoaded=1; } addLoadEvent(firstOnBodyLoadedEvent); function GetXmlHttpObject(handler) { var objXMLHttp=null; if(window.XMLHttpRequest) { objXMLHttp=new XMLHttpRequest() } else if(window.ActiveXObject) { objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP"); } return objXMLHttp; } function importZapatecMenuJS(onLoadFunction) { var headID=document.getElementsByTagName("head")[0]; var zpmenuNode=document.createElement('script'); zpmenuNode.type='text/javascript'; zpmenuNode.onload=function() { var myMenu=new Zapatec.Menu({theme:"/inc/barblue.css",source:"menu-items"}); var myMenuBar2=document.getElementById("noScriptMenu");"none"; var myMenuBar=document.getElementById("menu");"inline"; if(onLoadFunction!='null') { onLoadFunction(); } }; zpmenuNode.onreadystatechange=function() { if(zpmenuNode.readyState=='complete'||zpmenuNode.readyState=='loaded') { var myMenu=new Zapatec.Menu({theme:"/inc/barblue.css",source:"menu-items"}); var myMenuBar2=document.getElementById("noScriptMenu");"none"; var myMenuBar=document.getElementById("menu");"inline"; if(onLoadFunction!='null') { onLoadFunction(); } } }; zpmenuNode.src=myDirectory2+'/javascript/zpmenu.cfm'; headID.appendChild(zpmenuNode); } function importZapatecCalendarJS(onLoadFunction) { var headID = document.getElementsByTagName("head")[0]; var zpCalendarNode=document.createElement('script'); zpCalendarNode.type='text/javascript'; zpCalendarNode.onload=function() { if(onLoadFunction!='null') { importZapatecCalendarJS2(onLoadFunction); } }; zpCalendarNode.onreadystatechange=function() { if(zpmenuNode.readyState=='complete'||zpmenuNode.readyState=='loaded') { if(onLoadFunction!='null') { importZapatecCalendarJS2(onLoadFunction); } } }; zpCalendarNode.src=myDirectory2+'/zapatec/zpcal/src/calendar.js'; headID.appendChild(zpCalendarNode); } function importZapatecCalendarJS2(onLoadFunction) { var headID = document.getElementsByTagName("head")[0]; var zpCalendarNode2=document.createElement('script'); zpCalendarNode2.type='text/javascript'; zpCalendarNode2.onload=function() { if(onLoadFunction!='null') { onLoadFunction(); } }; zpCalendarNode2.onreadystatechange=function() { if(zpCalendarNode2.readyState=='complete'||zpCalendarNode2.readyState=='loaded')​ { if(onLoadFunction!='null') { onLoadFunction(); } } }; zpCalendarNode2.src=myDirectory2+'/zapatec/zpcal/lang/calendar-en.js'; headID.appendChild(zpCalendarNode2); } function importPrototypeJS() { var headID=document.getElementsByTagName("head")[0]; var prototypeNode=document.createElement('script'); prototypeNode.type='text/javascript'; prototypeNode.src=myDirectory2+'/javascript/prototype.cfm'; headID.appendChild(prototypeNode); } function importProtoaculousJS(onLoadFunction) { var headID=document.getElementsByTagName("head")[0]; var protoaculousNode=document.createElement('script'); protoaculousNode.type='text/javascript'; protoaculousNode.onload=function() { protoaculousHasLoaded=1; if(onLoadFunction!='null') { onLoadFunction(); } }; protoaculousNode.onreadystatechange=function() { if(protoaculousNode.readyState=='complete'||protoaculousNode.readyState=='loaded​') { protoaculousHasLoaded=1; if(onLoadFunction!='null') { onLoadFunction(); } } }; protoaculousNode.src=myDirectory2 +'/javascript/protoaculous.cfm'; headID.appendChild(protoaculousNode); } function importSwfObjectJS(hookWheelMouse,onLoadFunction) { if(swfObjectIsLoading==1) { return; } else { swfObjectIsLoading=1; } var headID=document.getElementsByTagName("head")[0]; var swfobjectNode=document.createElement('script'); swfobjectNode.type='text/javascript'; swfobjectNode.onload=function() { swfObjectHasLoaded=1; if(onLoadFunction!=null) { onLoadFunction(); } if(hookWheelMouse==1) { hookMouseWheel(); } }; swfobjectNode.onreadystatechange=function() { if(swfobjectNode.readyState=='complete'||swfobjectNode.readyState=='loaded') { swfObjectHasLoaded=1; if(onLoadFunction!=null) { onLoadFunction(); } if(hookWheelMouse==1) { hookMouseWheel(); } } }; swfobjectNode.src=myDirectory2+'/javascript/swfObject.cfm'; headID.appendChild(swfobjectNode); } function hookMouseWheel() { if (window.addEventListener) { window.addEventListener('DOMMouseScroll',onMouseWheel,false); } window.onmousewheel=document.onmousewheel=onMouseWheel; } function isMouseOverSwf(mEvent) { var elem; if(mEvent.srcElement) { elem=mEvent.srcElement.nodeName; } else if( {; } if(elem.toLowerCase()=="object"||elem.toLowerCase()=="embed") { return true; } return false; } function onMouseWheel(event) { var delta=0; if(!event) { event=window.event; } if(event.wheelDelta) { delta=event.wheelDelta/120; if(window.opera) { delta=-delta; } } else if(event.detail) { delta=-event.detail/3; } if(isMouseOverSwf(event)) { return cancelMouseEvent(event); } return true; } function cancelMouseEvent(e) { e=e?e:window.event; if (e.stopPropagation) { e.stopPropagation(); } if(e.preventDefault) { e.preventDefault(); } e.cancelBubble=true; e.cancel=true; e.returnValue=false; return false; } function enableCityAutoCompleter(myFunctionName) { var myScript=myDirectory+"/scripts/getCities.cfm"; new Ajax.Autocompleter("CITY_NAME","CITY_HINT",myScript, { minChars:1, frequency:0.3, callback:cityNameCallback, afterUpdateElement:myFunctionName }); } function cityNameCallback(element,entry) { var state=document.getElementById("STATE_ID").value; var country=document.getElementById("COUNTRY_ID").value; var myStr="&COUNTRY_ID="+country+"&STATE_ID="+state; return entry+myStr; } function enableBizNameAutoCompleter(myFunctionName) { var myScript=myDirectory+"/scripts/autocompleters/getBizNames.cfm"; new Ajax.Autocompleter("BUSINESS_NAME","BUSINESS_NAME_HINT",myScript, { minChars:1, frequency:0.3, afterUpdateElement:myFunctionName} ); } function showAdSpace() { if(typeof adMaxModifier=='undefined') { adMaxModifier=0; } if(typeof adPreference=='undefined') { adPreference="BUSINESS"; } if(typeof adKeywords=='undefined') { adKeywords=""; } var rightDiv=document.getElementById('col-r'); var leftDiv=document.getElementById('col-l'); var rightHeight=rightDiv.offsetHeight; var leftHeight=leftDiv.offsetHeight; var maxAds=Math.floor((rightHeight-leftHeight)/127)+adMaxModifier; if(maxAds>0) { xmlLeftAdSpace=GetXmlHttpObject(); if(xmlLeftAdSpace==null) { return; } url=myDirectory+"/ADS/showSideAds.cfm?maxAds="+maxAds+"&adKeywords="+adKeywords;url+="&adPreference="+adPreference+"&sid="+Math.random();"GET",url,true); xmlLeftAdSpace.onreadystatechange=function() { var sideAdDiv=document.getElementById('MY_ADSPACE'); if(xmlLeftAdSpace.readyState==4&&xmlLeftAdSpace.responseText!='') { sideAdDiv.innerHTML=xmlLeftAdSpace.responseText;"block"; } else if(xmlLeftAdSpace.readyState==4) {"none"; } }; xmlLeftAdSpace.send(null); } } function resizeImage(img,containerHeight,containerWidth,centerImage) { img.onload=null; var nimg=new Image(); nimg.onload=function() { if(nimg.height>containerHeight) { var ratioOne=nimg.width/nimg.height; nimg.height=containerHeight; nimg.width=nimg.height*ratioOne; } if(nimg.width>containerWidth) { var ratioTwo=nimg.height/nimg.width; nimg.width=containerWidth; nimg.height=nimg.width*ratioTwo; } img.height=nimg.height; img.width=nimg.width; img.src=nimg.src; if(centerImage==1) { var heightOffset=parseInt((containerHeight-nimg.height)/2); var widthOffset=parseInt((containerWidth-nimg.width)/2);'px';'px';'px';'px'; }"block"; }; nimg.src=img.src; } function resizeImage2(img,imgURL,containerHeight,containerWidth,centerImage) { img.onload=null; var nimg=new Image(); nimg.onload=function() { if(nimg.height>containerHeight) { var ratioOne=nimg.width/nimg.height; nimg.height=containerHeight; nimg.width=nimg.height*ratioOne; } if(nimg.width>containerWidth) { var ratioTwo=nimg.height/nimg.width; nimg.width=containerWidth; nimg.height=nimg.width*ratioTwo; } img.height=nimg.height; img.width=nimg.width; img.src=nimg.src; if(centerImage==1) { var heightOffset=parseInt((containerHeight-nimg.height)/2); var widthOffset=parseInt((containerWidth-nimg.width)/2);'px';'px';'px';'px'; }"block"; }; nimg.onerror=function() { img.height=100; img.width=100; img.src=''; if(centerImage==1) { var heightOffset=parseInt((containerHeight-100)/2); var widthOffset=parseInt((containerWidth-100)/2);'px';'px';'px';'px'; } }; nimg.src=imgURL; } function updateSubcatList(scriptParams) { xmlSubcatList=GetXmlHttpObject(); if(xmlSubcatList==null) { return; } url=myDirectory+"/scripts/subcat-list.cfm?search=1&"+scriptParams;"GET",url,true); xmlSubcatList.onreadystatechange=function() { var availableSubcats=document.getElementById("MY_AVAILABLE_SUBCATS"); if(xmlSubcatList.readyState==4&&xmlSubcatList.responseText!='') { availableSubcats.innerHTML=xmlSubcatList.responseText;"block"; } else if(xmlSubcatList.readyState==4) {"none"; } }; xmlSubcatList.send(null); } function updateCountryList(scriptParams) { xmlCountryList=GetXmlHttpObject(); if(xmlCountryList==null) { return; } url=myDirectory+"/scripts/country-list.cfm?search=1&"+scriptParams;"GET",url,true); xmlCountryList.onreadystatechange=function() { var availableCountries=document.getElementById("MY_AVAILABLE_COUNTRIES"); if(xmlCountryList.readyState==4&&xmlCountryList.responseText!='') { availableCountries.innerHTML=xmlCountryList.responseText;"block"; } else if(xmlCountryList.readyState==4) {"none"; } }; xmlCountryList.send(null); } function updateStateList(scriptParams) { xmlStateList=GetXmlHttpObject(); if(xmlStateList==null) { return; } url=myDirectory+"/scripts/state-list.cfm?search=1&"+scriptParams;"GET",url,true); xmlStateList.onreadystatechange=function() { var availableRegions=document.getElementById("MY_AVAILABLE_STATES"); if(xmlStateList.readyState==4&&xmlStateList.responseText!='') { availableRegions.innerHTML=xmlStateList.responseText;"block"; } else if(xmlStateList.readyState==4) {"none"; } }; xmlStateList.send(null); } function disableForms() { var inputs=document.getElementsByTagName('input'); for(element in inputs) { if(inputs[element].type=='submit') { inputs[element].disabled=true; } } } document.onfocus=function() { var inputs=document.getElementsByTagName('input'); for(element in inputs) { if(inputs[element].type=='submit') { inputs[element].disabled=false; } } }; function disableElement(element) { var myElement=document.getElementById(element); myElement.disabled=true; } function checkHTML(myElement,myElementError) { xmlCheckHTML=GetXmlHttpObject(); if(xmlCheckHTML==null) { return; } var myElement=document.getElementById(myElement); var myElementError=document.getElementById(myElementError); url=myDirectory+"/scripts/htmlCheck.cfm?myStr="+myElement.value;"GET",url,true); xmlCheckHTML.onreadystatechange=function() { if(xmlCheckHTML.readyState==4&&xmlCheckHTML.responseText!='') { myElementError.innerHTML=xmlCheckHTML.responseText;"block"; } else if(xmlCheckHTML.readyState==4) {"none"; } }; xmlCheckHTML.send(null); } function checkUsername(myElement,myElementError) { xmlCheckUsername=GetXmlHttpObject(); if(xmlCheckUsername==null) { return; } var myElement2=document.getElementById(myElement); var myElementError2=document.getElementById(myElementError); url=myDirectory+"/scripts/checkUsername.cfm?USERNAME="+myElement2.value;"GET",url,true); xmlCheckUsername.onreadystatechange=function() { if(xmlCheckUsername.readyState==4&&xmlCheckUsername.responseText!='') { myElementError2.innerHTML=xmlCheckUsername.responseText;"block"; } else if(xmlCheckUsername.readyState==4) {"none"; checkHTML(myElement,myElementError); } }; xmlCheckUsername.send(null); } function checkBizName(newBizName,newBizNameError,curBizName) { xmlCheckBizName=GetXmlHttpObject(); if(xmlCheckBizName==null) { return; } var newBizName2=document.getElementById(newBizName); var newBizNameError2=document.getElementById(newBizNameError); url=myDirectory+"/scripts/checkBizName.cfm?BUSINESS_NAME="+newBizName2.value+"&CURRENT_BIZ_NAME="+curBizName;url=url+"&sid="+Math.random();"GET",url,true); xmlCheckBizName.onreadystatechange=function() { if(xmlCheckBizName.readyState==4&&xmlCheckBizName.responseText!='') { newBizNameError2.innerHTML=xmlCheckBizName.responseText;"block"; } else if (xmlCheckBizName.readyState==4) {"none"; checkHTML(newBizName,newBizNameError); } }; xmlCheckBizName.send(null); } function checkNumeric(myElement,myElementError) { xmlCheckNumeric=GetXmlHttpObject(); if(xmlCheckNumeric==null) { return; } var myElement2=document.getElementById(myElement); var myElementError2=document.getElementById(myElementError); url=myDirectory+"/scripts/numericCheck.cfm?myStr="+myElement2.value;"GET",url,true); xmlCheckNumeric.onreadystatechange=function() { if(xmlCheckNumeric.readyState==4&&xmlCheckNumeric.responseText!='') { myElementError2.innerHTML=xmlCheckNumeric.responseText;"block"; } else if(xmlCheckNumeric.readyState==4) {"none"; } }; xmlCheckNumeric.send(null); } function checkWebsite(myElement,myElementError) { xmlCheckWebsite=GetXmlHttpObject(); if(xmlCheckWebsite==null) { return; } var websiteURL=document.getElementById(myElement); var websiteError=document.getElementById(myElementError); url=myDirectory+"/scripts/checkWebsite.cfm?WEBSITE_URL="+websiteURL.value;"GET",url,true); xmlCheckWebsite.onreadystatechange=function() { if(xmlCheckWebsite.readyState==4&&xmlCheckWebsite.responseText!='') { var errorMessage = 'That website URL is not valid. If you type your website '; errorMessage+='URL as plain text, we will take care of the rest. Error: '+xmlCheckWebsite.responseText+''; websiteError.innerHTML=errorMessage;"block"; } else if(xmlCheckWebsite.readyState==4) { = "none"; } }; xmlCheckWebsite.send(null); } function checkCityName(regionElement,cityElement,cityElementError) { xmlCheckCity=GetXmlHttpObject(); if(xmlCheckCity==null) { return; } var cityName=document.getElementById(cityElement); var regionID=document.getElementById(regionElement); var cityNameError=document.getElementById(cityElementError); url=myDirectory+"/scripts/checkCityName.cfm?CITY_NAME="+cityName.value;url=url+"&STATE_ID="+regionID.value;"GET",url,true); xmlCheckCity.onreadystatechange=function() { if(xmlCheckCity.readyState==4&&xmlCheckCity.responseText!='') { cityNameError.innerHTML=xmlCheckCity.responseText;"block"; } else if(xmlCheckCity.readyState==4) {"none"; } }; xmlCheckCity.send(null); } function charCheck(myLabel,myTxtArea,charCount) { var charsRemainingLabel=document.getElementById(myLabel); var myTextArea=document.getElementById(myTxtArea); var charsLeft=charCount-myTextArea.value.length; if(charsLeft < 0) { alert("You can only enter up to "+charCount+" characters for this text field."); myTextArea.value=myTextArea.value.substring(0,charCount); charsRemainingLabel.innerHTML="0 characters remaining"; } else if(charsLeft==1) { charsRemainingLabel.innerHTML=charsLeft+" character remaining"; } else { charsRemainingLabel.innerHTML=charsLeft+" characters remaining"; } }
// ]]>

All resources on the page were exactly the same so the only change was the two code blocks above. The code attached in Exhibit A matches the code block in Exhibit B.

Test Results:

Exhibit A

Exhibit B

The test results showed that the inline JavaScript in Exhibit B resulted in a faster page speed both first view and repeat view.

I figure the difference in execution time would be the time to first byte for the included JavaScript. In Exhibit B, you do not have that look up time.

The blocking of JavaScript is due to the execution of the JavaScript and not necessarily how it is included correct?

If you have to include a script that blocks other downloads, why not include it inline as opposed to fetching it with src?

Some people may argue that code changes are easier to update. However, with dynamic scripting languages such as coldfusion, you can just include a template that will allow JavaScript changes in one location.

How would having the inline JavaScript at the top effect website crawlers such as Google? Would Google look past all the JavaScript?

Any thoughts or comments?

Travis Walters
One problem with inlining is the increased page weight per page when looking across multiple pages on your site.
Having JS/CSS external allows the browser to cache JS/CSS that might be used across multiple pages. Using the inlining technique you pay the page weight penalty each time.
If the JS/CSS is specific only to that page then you have a valid argument for inlining it.
If you are serving up content to mobile users then it is also worth paying the page weight penalty as having to get many resources for a page is expensive over mobile connections.
If there is high latency, high bandwidth then inlining is best, as in mobile. If there is low latency, high bandwidth then a degree of parallelism will win over downloading one large resource.
You ask a question about "if you have to include a script that blocks other downloads"....I would ask back does it really have to block downloads? If so would it not be worth investigating redesigning the page so that it does not rely on the blocking javascript. You may be able to move to a more asynchronous model that gives a perceived performance improvement in terms of the end user experience even though the overall performance may not be improved.
Looking at your tests the results times are very similar. So similar on the repeat views section that I would consider them to be the same. It is important to take into account the margin of error produced in the results from the test platform. Whilst I don't know the margin of error for WebPagetest i would suggest that the results for the repeat views are likely to fall within it and therefore can be considered to be the same. Maybe a suggestion for aiding in the analysis of results would be the publication,by WebPagetest, of the likely margin of error for the test run?
From the looks of the js ( i had a v.brief look) you have inlined you could asynchronously load that resource using a pattern like the latest one used by google for google analytics. This should reduce the time to document complete further for your tests.

I see no reason as to why Google would be affected by the inline javascript, they heavily use inlining with mobile applications. The performance indicator they are taking into account for their ranking is the page load time (I think it is to the Onload event but I'm not sure), so if the inlined page is quicker then, all things being equal between yourself and your competitors in the other aspects of ranking, you should see your ranking improve.

The script I used for the experiment is 15 KB in size so it does not add -much- weight across the website but it does add some to the main document for repeat views.

This script is mainly functions that are used on most webpages across the site and includes functions to import other JavaScript asynchronously. When I first came to, I had over 200KB of JavaScript that blocked other resources from being downloaded.

I believe in this case the negative page weight penalty is outweighed by having one less resource to download, the ability to download an additional resource in parallel, and the deduction of the time to first byte for the external JavaScript file that is being inlined.

Mobile users are not a concern at this point. I have little experience designing applications for mobile users unfortunately but perhaps in the future I will consider this.

On a side note, I also saw a bit of a difference when I got rid of white space, but that was not involved in this particular experiment.
The results seem to show that in the experiment you are testing on WebPagetest that the external resource is taking a shorter time than the inlined resource, so it might be possible to surmise that the costs of inlining are outweighing the costs of an external extra resource, and are not in line with what you believe (if I have correctly understood your position). This is going by the First view Load time metric as the other metrics are so similar, TTFB only has a spread of 31 ms for the repeat view and 56 ms for the first view with no apparent separation between the result groups for A and B. The First View Load Time only has a spread of 159ms with a separation between the results groups of 73 ms (between highest result for A and lowest result for B).

What is the most important metric that you want to improve with the experiment?
My apologies. I had a typo in my original post.

It was suppose to be:

The test results showed that the inline JavaScript in Exhibit B resulted in a faster page speed both first view and repeat view.

I changed it above now so other people do not get confused. Sorry about that.
I've just noticed that I can't see the siteJavascript.cfm resource on either of the results pages for Exhibit A (JS as external) fact it turns up in Exhibit B (JS as inline) I think the results pages are under the wrong exhibits, which has also led to some confusion.

So are we looking at the Fully loaded time metric or the Document complete metric? I would argue that the Document Complete metric is more valid in this situation.
The external JS is being downloaded in parallel with an external CSS from your cdn subdomain, in fact it takes less time to download than the css file. You are downloading images from sharded image subdomains in parallel to these 2 resources being served from the cdn subdomains. So I am not seeing any blocking behaviour in particular. I think the differences in the times can be related more strongly to a variance in the response time of your server rather than a variance in the behaviour of the inlined/external JS.

Also you are using the same IP for a number of your domains (CDN and images), you could try the nested CNAMEs DNS hack to cut down the number of DNS requests for colocated domains from 4 to 1.
Hey There,

You were correct that the exhibit results were labeled incorrectly. That is probably why I labeled the sentence under it wrong to begin with.

I edited the initial post once again to switch the results labels.

I have been focusing on getting my document complete a bit faster because this is what Google measures page speed by (at least what I have heard).

However, I still keep an eye on fully loaded as I want to increase user experience as well.

When I had the 200KB JavaScript files, that is when I had a lot of blocking. The functions that are there now are mainly to initialize asynchronous downloads.

Could you tell me more about the CNAME DNS hack you are referring to?

Currently I have multiple subdomains setup using CNAMEs that point to the maxcdn service.

Travis Walters
The CNAME DNS hack is a pretty cool trick that was discussed at velocity this year where instead of the usual:

name1 -> hostname -> IP
name2 -> hostname -> IP
name3 -> hostname -> IP

where all 3 sharded sub-domains are CNAMES to the same host name (or A records to an IP) you do:

name1 -> name2 -> name3 -> hostname -> IP

and have the CNAMES point to each other in a chain. That way when the lookup is done for name1 you will get name2 and name3 in the same result.

It's really easy to pull off if you're not using a CDN and the shards are pointing to the same IP as your base page. In the case of a CDN you just need to put your initial CSS and JS on name1 and any images can be spread across them (in order to get the benefit of name1 having resolved 2 and 3).
So basically this would only cause one DNS request correct?

So basically I could have,,, etc
download resources in parallel with just one DNS request.

I would just need to make sure resources are always served from the same subdomain to increase cache potential or
would that matter with the hack because it would point back to the cdn before resolving?
Yes, just one DNS request (assuming you can have the chain resolved before it would try to fetch the images, otherwise it will kick off the 3 DNS lookups in parallel anyway) and yes, you still need to keep resources on the same domains for the browser cache to recognize them.
Pages: 1 2 3
Reference URL's