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

WebKit Timeline

MySpace's Performance Tracker

Google Page Speed

Closure Compiler