Progressive Enhancement: Tools and Techniques
Annie Sullivan
@anniesullie
What Is Progressive Enhancement?
Content as HTML
Enhancing display with CSS
Enhancing interactivity with JavaScript
Progressive Enhancement and Performance
Old
New
60% Faster!
Summary
See content as downloaded
Use features as available
JavaScript errors don't block rendering
Implementing Progressive Enhancement
Laying the Groundwork
Sorting out the JavaScript
Fixing UI Generated by JavaScript
Laying the Groundwork
Laying the Groundwork
Understanding Waterfall Charts
Measuring Real Users' Experiences
Waterfall charts
Finding Serial Requests
Finding Requests that Block Rendering
WebKit Timeline
MySpace Performance Tracker
Google Page Speed
webPageTest.org
Measuring Page Load
<-- First line of HTML -->
<script>
var startTime = new Date().getTime();
</script>
Measuring Page Load
<script>
window.onload = function() {
var endTime = new Date().getTime();
// Total time in milliseconds
var totalTime = endTime - startTime;
var uri = 'reportTimings?onLoad=' + totalTime;
new Image().src = uri;
</script>
Measuring Page Load
<-- Some important content here -->
</content>
<script>
var endTime = new Date().getTime();
// Total time in milliseconds
var totalTime = endTime - startTime;
var uri = 'reportTimings?contentLoaded=' + totalTime;
new Image().src = uri;
</script>
Sorting out the JavaScript
A Simple Build System
Classifying into Types
Moving the Code Around
A Simple Build System
JavaScript: Closure Compiler
java -jar compiler.jar \
--js file1.js \
--js file2.js \
--js file3.js \
--js_output_file output/compiled.js
CSS:YUI Compressor
cat file1.css file2.css > concat.css
java -jar yuicompressor.jar -type css \
concat.css -o minified.css
Classifying JavaScript into Types
Not Needed
Rarely Used Features
Needs to run to Initialize App
Needs to run before HTML is Finished Loading
Closure Compiler Finds Unused JS
Original Code
function unusedFunc() {
alert('never called');
}
function usedFunc() {
alert('this is called');
}
usedFunc();
Compiler Flags
--formatting PRETTY_PRINT
--compilation_level ADVANCED_OPTIMIZATIONS
Compiled output
alert("this is called");
Page Speed Finds Deferrable JS
Moving the JavaScript Around
Inline Script
Asynchronous Script
Closure Compiler Modules
java compiler.jar
--js base.js --js delayed1.js --js delayed2.js
--module base:1
--module delayed:2:base
Asynchronous Script Loading
<script>
function addScript(url) {
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
</script>
Read all about it: Even Faster Websites, Chapter 4
Demos at cuzillion.com
Asynchronous Script Loading
function handleLoad(callback) {
callback();
script.onload = script.onreadystatechange = null;
}
function addScript(url, callback) {
var script = document.createElement('script');
script.src = url;
script.onload = function() { handleLoad(callback); }
script.onreadystatechange = function() {
if (script.readyState == 'loaded' ||
script.readyState == 'complete')
handleLoad(callback);
};
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
Fixing UI Generated by JavaScript
Progressively Enhancing HTML
Event Queueing
Late-Loading Features
Progressively Enhancing HTML
Write Simple HTML
Use Disabled CSS
Enable When JavaScript Loads
Example: jQuery UI
<!-- Can use disabled classes from jQuery themes -->
<button id="MyButton" class="disabled">
Click me!
</button>
$("#MyButton").button().click(handlerFunc);
Queuing Events
var queue = [];
function queueClick(event) {
// App-specific check for target
// Can use node class, type, etc.
queue.push(getTarget(event));
}
if (window.addEventListner) {
document.addEventListener('click', queueClick, false);
} else {
document.attachEvent('onclick', queueClick);
}
Firing queued events
function fireEvent(element, event) {
if (document.createEventObject){
// IE
var evt = document.createEventObject();
return element.fireEvent('on' + event, evt)
} else {
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event,
true, // bubbling
true); // cancelable
return !element.dispatchEvent(evt);
}
}
Late-loading Features
Allocate space on the page (<div> or <iframe>)
Render content into space when script loads.
Summary
Progressive Enhancement: Separate Content from Interactivity
Performance: Users see content faster
Implementing Progressive Enhancement
Laying the Groudwork
Sorting out the JavaScript
Fixing the Problems
Slides
http://www.monkey.org/~annie/ProgressiveEnhancement.html
Resources