Now you can take what you learned in Chapter 9, "Optimizing JavaScript for Download Speed," and apply these techniques to a real-world example. To show you how effective JavaScript packing and compression can be, we'll crunch and compress an actual script from Webreference.com. Peter Belesis' popular hierarchical menus are available at Webreference.com, the site I founded (http://www. webreference .com/dhtml/hiermenus/). His menus use five files to account for different browsers (see Table 11.1).
Filename | Size in Bytes |
---|---|
HM_Loader.js | 3564 |
HM_Arrays.js | 9950 |
HM_ScriptDOM.js | 45623 |
HM_ScriptIE4.js | 40014 |
HM_ScriptNS4.js | 36956 |
The loader file loads two files, the menu arrays, and the appropriate browser API for a total of three HTTP requests . One quick optimization you can do is combine the arrays file with each browser file to save an HTTP request, like this (see Table 11.2).
Filename | Size in Bytes |
---|---|
HM_Loader.js | 3564 |
HM_ScriptDOM_arrays.js | 55573 |
HM_ScriptIE4_arrays.js | 49964 |
HM_ScriptNS4_arrays.js | 46906 |
Combining the menu arrays with each file trades maintenance costs for speed. If your site's navigation won't change very often, you can do this manually; otherwise , you could merge these programmatically on the server. To give you an idea of how effective packing and compression can be, let's optimize the size of the largest DOM file.
Here's an excerpt of HM_ScriptDOM_arrays.hs before packing:
HM_Array1 = [ [150,,, ,,,,,,,,,,,,,,,, 1,true], ["Experts","http://www.webreference.com/experts/",1,0,1], ["Contents","http://www.webreference.com/index2.html",1,0,0], ... HM_MenuIDPrefix = "HM_Menu"; HM_ItemIDPrefix = "HM_Item"; HM_ArrayIDPrefix = "HM_Array"; Function.prototype.isFunction = true; Function.prototype.isString = false; String.prototype.isFunction = false; String.prototype.isString = true; String.prototype.isBoolean = false; String.prototype.isNumber = false; Number.prototype.isString = false; ... if(HM_IE) { HM_a_ElementsCreated = []; function HM_f_StoreElement(el){ HM_a_ElementsCreated[HM_a_ElementsCreated.length] = el; } }
Here's that same excerpt after packing and manual abbreviation:
Array1=[ [150,,, ,,,,,,,,,,,,,,,, 1,true], ["Experts","/experts/",1,0,1], ["Contents","/index2.html",1,0,0], ... mp="Menu";ip="Item";ap="Array";Function.prototype.isf=true;Function.prototype.iss=false; String.prototype.isf=false;String.prototype.iss=true;String.prototype.isb=false; String.prototype.isn=false;Number.prototype.iss=false;...if(IE){a_ec=[];function f_s( el){a_ec[a_ec.length]=el;}}
Packing the file to remove comments and whitespace saves 17 percent, reducing the file to 46,051 bytes. Manually packing to abbreviate variable and function names saves 52 percent, reducing from 55,573 to 26,870 bytes. Table 11.3 shows a summary of the packing and compressed file sizes.
File | Size | PACK | GZIP 9 | PACK+GZIP |
---|---|---|---|---|
HM_ScriptDOM_arrays.js | 55,573 | 26,870 | 10,655 | 7,808 |
Of course the file is all but indecipherable now, but it is less than half the size. GZIP compressing the original file saves over 80 percent in file size (from 55,573 to 10,655 bytes). Packing plus GZIP compression saves 86 percent, reducing from 55,573 to 7,808 bytes. By combining two files, and packing and compressing the combined file, you save one HTTP request and over 46K (86 percent) for the DOM-based code, a savings of over 10 seconds at 56Kbps.
Instead of loading two files totaling over 54K, you can send one 26K packed file or one 7,808 byte file by serving a combined, packed, gzipped file, which gives you a six-fold improvement in speed. Even without compression, you save over 50 percent in file size. These savings of 50 to 90 percent are typical for JavaScript files. Ideally, you'd eliminate the loading file by using either server-side sniffing or a client-side document.write . Packing with an automated program like ESC and compressing with mod_gzip would achieve similar results, but with less naming control.