Highlights
JavaScript loader
Load scripts in parallel but execute in order
head.js("/path/to/jquery.js", "/google/analytics.js", "/js/site.js", function() {
// all done
});
Head JS loads JavaScript files like images without blocking the page. Your page will be faster even with a single combined file.
JavaScript organizer
You've read the books and placed SCRIPT SRC tags on the bottom. Good. But can you do this?
// use jQuery on the body of the page even though it is not included yet
head.ready(function() {
$("#my").jquery_plugin();
});
// load jQuery whenever you wish bottom of the page
head.js("/path/to/jquery.js");
CSS modernizer
HeadJS adds classes to the HTML root element so that you can do this
/* target CSS for browsers without box-shadow support */
.no-boxshadow .box {
border: 2px solid #ddd;
}
The focus is on CSS3 styling and it's easy to add more tests. Minimalism is a must. It's your HEAD again.
HTML5 enabler
DIV is good but HeadJS let's you be semantic and futuristic
<style>
article { text-shadow:0 0 1px #ccc; }
</style>
<!-- works in IE too -->
<article>
<header></header>
<section></section>
<footer></footer>
</article>
Screen size detector
Design pages for 1980px wide screen and make it also work on 10" laptops and mobile phones. Viewport is the most important thing when developing for mobile clients.
/* screen size less than 1024 pixels */
.lt-1024 #hero { background-image:(medium.jpg); }
/* fine tune for mobile phone */
.lt-640 #hero { background-image:(small.jpg); }
If you resize the browser window your CSS rules will dynamically follow. Head JS does it behind the scenes.
Dynamic CSS
Style for various application states
<script>
/* detect whether user is logged in. here we check for an existence of a cookie */
head.feature("logged", mycookielib.get_cookie("auth_token"));
</script>
<style>
/* .. and write CSS accordingly */
.logged #login-box { display: none; }
</style>
Whenever you call `head.feature()` your CSS rules will follow.
CSS router
Target CSS for specific paths and pages
/* CSS targeted for home page only */
.root-section #index-page
/* make sidebar visible under /plugins */
.plugins-section #sidebar { display: block; }
Browser detector
All browsers are detected but we all love IE
/* older than IE9 */
.lt-ie9 .box { padding: 10px; }
/* CSS fixes for IE6 */
.ie6 ul { list-style: none; }
It's generally wiser to use feature detection IE is a feature in itself.
JavaScript feature detection
Every feature can also be found on the head (default) JavaScript variable.
if (head.logged) {
// do things
}
Theory
Head JS script loader
With Head JS your scripts load like images - completely separated from the page rendering process. The page is ready sooner. Always. This is guaranteed even with a single combined JavaScript file. See it yourself:
Sample page with 5 SCRIPT SRC tags
Same scripts loaded with Head JS
Non-blocking loading is the key to fast pages. Moreover Head JS loads scripts in parallel no matter how many of them and what the browser is. The speed difference can be dramatic especially on the initial page load when the scripts are not yet in cache. It's your crucial first impression.
Pages no longer "hang" and there is less or zero "flashing" between pages. User only cares when the page is ready. Unfortunately current networking tools don't highlight this crucial point. They focus on the overall loading of assets instead.
Head JS can make your pages load 100% or even 400% faster. It can make the largest impact on client side optimization.
SCRIPT SRC
This is from Apple.com's HEAD:
<script src="http://images.apple.com/global/scripts/lib/prototype.js"></script>
<script src="http://images.apple.com/global/scripts/lib/scriptaculous.js"></script>
<script src="http://images.apple.com/global/scripts/browserdetect.js"></script>
<script src="http://images.apple.com/global/scripts/apple_core.js"></script>
<script src="http://images.apple.com/global/scripts/search_decorator.js"></script>
<script src="http://images.apple.com/global/scripts/promomanager.js"></script>
<script src="http://images.apple.com/home/scripts/ticker.js"></script>
<script src="http://images.apple.com/home/scripts/promotracker.js"></script>
All these scripts block the page. The page needs to wait for these to be loaded until it starts rendering itself. This is how majority of web sites are done. There is not a large difference whether the script tags are placed on top or on bottom of the page. On both cases the scripts must be loaded first. Compare it yourself.
With older generation browsers the scripts will be loaded sequentially. Here is a screenshot from Firefox 3.0.
It's easy to understand that this is poison for performance. The single best optimization technique for script loading is to use a non-blocking and paraller script loader such as Head JS.
Developing with Head JS
With Head JS your page can be ready before your scripts. You need to prepare for that
The "DOM ready" event such as
$(document).ready()is already fired when the scripts arrive. If the loaded scripts depend on that event make sure your library can handle this. jQuery 1.4+ works.
If your scripts modify the page you can see a "flash of unbehaviored content" effect (FUBC): a quick glimpse of unmodified HTML before the loaded scripts act on it. You can avoid this by initially hiding the modifiable elements before your scripts make them visible and do their job.
Combining scripts
There is a common misbelief that a single combined script performs best. Wrong:
latest browsers and Head JS can load scripts in parallel. loading 3 parts in parallel instead of as a single chunk is usually faster.
if an individual file is changed the whole combination changes and you loose the benefits of caching. it's better to combine only the stable files that doesn't change often.
many popular libraries are hosted on CDN. you should take the advantage of it instead of hosting yourself
iPhone 3.x cannot cache files larger than 15kb and in iPhone 4 the limit is 25kb. And this is the size before gzipping. if you care about iPhones you should respect these limits.
With Head JS the file amount is not as critical as it is with SCRIPT SRC because page rendering is not blocked. Combining scripts is an important optimization method but you can go too far with it.
Page styling and CSS
Stylesheets needs to be on top. HEAD is the only place where a script is executed before the stylesheets and where it can give a helping hand for CSS developer. Head JS takes this important role seriously:
- you can use the latest CSS3 techniques and provide alternate CSS for IE and other old school browsers
- you can safely use HTML5 tags even with IE
- you can target CSS for specific screen widths, browsers and paths
- you can style your pages differently depending on the application state, such as whether user is logged or not
HEAD script blocks so it must be small. This is the essence of Head JS. It weights 2.3 kb when minified and gzipped. It's preferred to load the file from a CDN. Google: I'm staring at You! (please give it a star)
If you attempt to style the page on the scripts loaded with Head JS you can see a "Flash of Unstyled Content" (FOUC) effect: the page is first rendered without the to-be-loaded style definitions and then re-rendered when the scripts are executed.
Note: As crazy as it sounds FUBC and FOUC are not my acronyms.
Usage
Script loading and execution
All script loading is done with head.js()
// the most simple case. load and execute single script without blocking.
head.js("/path/to/file.js");
// load a script and execute a function after it has been loaded
head.js("/path/to/file.js", function() {
});
// load files in parallel but execute them in sequence
head.js("file1.js", "file2.js", ... "fileN.js");
// execute function after all scripts have been loaded
head.js("file1.js", "file2.js", function() {
});
// files are loaded in parallel and executed in order they arrive
head.js("file1.js");
head.js("file2.js");
head.js("file3.js");
// the previous can also be written as
head.js("file1.js").js("file1.js").js("file3.js");
Script organization
Call a function after all scripts have been loaded
head.ready(function() {
});
The functions supplied this way are executed in the same order as they are given.
// call a function after a particular file has been loaded
head.ready("file2.js", function() {
});
This makes sense if you have large script files and want to execute JavaScript before all files are loaded.
Otherwise you simply use head.ready() which gets executed after all files are loaded.
// a handy shortcut for head.ready()
head(function() {
})
Labeling scripts
By assigning labels the content developers need not to know the actual file paths.
// call a function immediately after jQuery Tools is loaded
head.ready("tools", function() {
// setup Tooltips
$(".tip").tooltip();
});
// load scripts by assigning a label for them
head.js(
{jquery: "http://http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"},
{tools: "http://cnd.jquerytools.org/1.2.5/tiny/jquery.tools.min"},
{heavy: "http://a.heavy.library/we/dont/want/to/wait/for.js"},
// label is optional
"http://can.be.mixed/with/unlabeled/files.js"
);
If label is not given then the file name without the path is used as the key. On the jQuery library above this would be just "jquery.min.js".
CSS3 feature detection
When a particular feature is supported a corresponding class name is added to the element.
When a feature is not supported the class name is prefixed with no-. For example:
.borderimage .box {
/* style for browsers that support border images */
}
.no-borderimage .box {
/* style for browsers that does not support border images */
}
On everyday CSS work you assume that a feature exists and code your CSS accordingly and you later add support for older browsers with the aid of "no-" classes.
CSS3 property list
Natively detected CSS3 properties
- borderimage define an image to be used instead of the normal border of an element
- borderradius support for rounded borders
- boxshadow a simple shadow for a box
- multiplebgs ability to define multiple background properties for a box
- opacity transparency support
- reflections a reflection effect for an object
- rgba allows to specify an opacity value for a color
- textshadow a simple shadow for a text
- transforms ability to scale
- transitions ability to animate changes to CSS properties
You can detect more with head.feature() call. Here we detect HTML5 video support
head.feature("video", function() {
var tag = document.createElement('video');
return !!tag.canPlayType;
});
Modernizr is a good source of tests. It's preferred to append the tests inside head.min.js file.
HTML5 enabler
Head JS adds HTML5 support for all browsers:
<style>
article { text-shadow:0 0 1px #ccc; }
</style>
<!-- works in IE too -->
<article>
<header></header>
<section></section>
<footer></footer>
</article>
List of new tags
- abbr an abbrevation
- article defines external content
- aside defines some content aside from the content it is placed in
- audio defines sound, such as music or other audio streams.
- canvas a container for javascript graphics
- details describe details about a document, or parts of a document
- figcaption contains a caption for the "figure" element
- figure stand-alone content, typically used to explain parts of a document
- footer defines the footer of a section or document
- header defines the header of a section or document
- hgroup defines the heading of a section or a document.
- mark tag defines marked or highlighted text
- meter for measurements with a known minimum and maximum value
- nav a section intended for navigation
- output defines different types of output, such as output written by a script
- progressdisplay the progress of a time consuming function in JavaScript.
- section defines sections in a document such as chapters, headers, footers, or any other sections of the document.
- summary contains a header for the "details" element
- time defines a time or a date, or both.
-
video defines video, such as a movie clip or other video streams
IE has problems printing HTML5 elements. You can fix that by loading a print protector script on your page:
if (head.browser.ie) {
head.js("http://www.iecss.com/print-protector/javascript/iepp.1-6-2.min.js");
}
Screen size detection
Target CSS for various screen sizes. For example
/* styling for screens with resolution less than 1024 pixels */
.lt-1024 #hero { background-image:(medium.jpg); }
/* styling for small mobile screens */
.lt-640 #hero { background-image:(small.jpg); }
Today most sites are optimized for 1024px but with Head JS you can safely optimize for larger screens. Start by designing to the largest screen and then add CSS rules for smaller ones. You can freely decide the sizes you optimize for and the amount of them. By default Head JS watches following screen widths:
320, 480, 640, 768, 800, 1024, 1280, 1440, 1680, 1920
These are typical sizes for mobile phones, tablets and PC monitors. Here are sizes for Apple devices:
iPhone3: 480 x 320
iPhone4: 960 x 640
iPad: 1024 x 768
screens: 800+
You can provide your own set of screen widths with head_conf variable:
var head_conf = { screens: [500, 700, 900] };
If you resize the browser window your CSS rules will be dynamically applied.
Browser detection
Target CSS for a specific browser. For example
/* CSS for IE version 7 and below */
.lt-ie8 .box { padding: 10px; }
/* CSS for IE6 only */
.ie6 ul { list-style: none; }
List of supported class names for various browsers
-
ieInternet Explorer -
webkitWebkit based browsers such as Safari and Chrome -
operaOpera -
mozillaMozilla based browsers such as Firefox
There are additional "less than" helper classes for IE.
// tweak CSS for IE versions 5 and below
.ie-lt6 li {
list-style-type: circle;
}
Versions 3 - 10 are covered. It is best to avoid browser detection if you are able to do feature detection. Sometimes it's not possible especially when it comes to IE.
CSS Routing
Say you are on a page: http://mydomain.com/addons/node/router.html. Following CSS selectors apply:
.addons-section { }
.addons-node-section { }
#router-page { }
For each given path you get CSS classes representing "folders" and an ID representing the page. The deeper your URL the more classes you'll get An empty page is "#index-page" and the site root is ".root-section". NOTE: If you have an existing id assigned for the HTML element it will be overwritten. On this case you can place the id attribute for the BODY tag.
You can change the these suffixes with head_conf variable.
var head_conf = { section: '-area', page: ''};
Now the selectors are:
.addons-area { }
.addons-node-area { }
#router { }
JavaScript feature detection
Every feature can be found from th head global JavaScript variable. Here is a screenshot:
API
Method index
head.js(file1 ... fileN, [callback])
Immediately loads given javascript files and and executes them in the order they are supplied. if the last argument is a function it is called after all files are loaded and executed. Each file is either a string representing relative or absolute path of a JavaScript file.
Alternatively each file can be given as an object taking a form: { label: file_path }. The labels are a handy way to hide the actual file path from the content writers. See the ready method below.
head.ready(callback)
Executes the given callback after all JavaScript files have been loaded
head.ready(path_or_label, callback)
Execute the callback immediately after the given file or label is loaded and executed
head(callback), head(path_or_label, callback)
A convenience shortcut for head.ready
head.feature(name, flag)
Enable or disable a feature. features are stored as a class name to <html> element and on global head variable. if the flag arguments evaluates to true a feature is enabled. Call this method in your HEAD before the stylesheets are loaded.
Configuration
HeadJS is configured with a global head_conf variable before the script is loaded. For example:
<script>
var head_conf = { screens: [640, 1024, 1280, 1680] };
</script>
<script src="/js/head.min.js"></script>
List of configuration variables
-
headability to change global variable name. default: "head". Set to "JS" and you canJS.ready()for example -
screensan array of screen sizes to watch for
-
sectiona suffix for CSS class representing the current "folder". default: "-section" -
pagea suffix for CSS ID representing the current page. default: "-page"
Download
Version 0.5
Production ready.
I'm JavaScripter. There are couple of guys wiser than me in theory of script loading. Eventually all this wisdom will be merged to Head JS
Issues
-
There is no way to detect if script loading failed in IE. For that reason a feature such as
head.error()is impossible to implement. When a script URL cannot be accessed Head JS is silent.