"Progressive Enhancement for JavaScript Apps" by Garann Means.
When progressive enhancement was introduced as a concept, JavaScript applications seemed as relevant as flying cars. As JS became more powerful, it seemed we'd reach a point where we could forget PE entirely. For its original meaning, we now have rock-solid libraries and polyfills to provide abstractions that make PE easy. But as JS has advanced, we've started writing things that can't be polyfilled. We know now how to progressively enhance widgets and user interactions. We'll talk about how we progressively enhance entire applications, and why it's more important than ever that we do so.
8. 2003-2008
🚀 “let’s use JS for everything!”
🚀 “what, people have JS turned off?”
🚀 “what, browser support is inconsistent?”
9. 2003-2008
🚀 “let’s use JS for everything!”
🚀 “what, people have JS turned off?”
🚀 “what, browser support is inconsistent?”
🚀 “who cares, our customers are cutting edge”
10. 2003-2008
🚀 “let’s use JS for everything!”
🚀 “what, people have JS turned off?”
🚀 “what, browser support is inconsistent?”
🚀 “who cares, our customers are cutting edge”
🚀 from yesterday: “if you’re seeing a broken
experience, your environment is broken”
11. 2003-2008
🚀 “let’s use JS for everything!”
🚀 “what, people have JS turned off?”
🚀 “what, browser support is inconsistent?”
🚀 “who cares, our customers are cutting edge”
🚀 from yesterday: “if you’re seeing a broken
experience, your environment is broken”
🚀 “oh weird our business failed”
14. why’d we switch?
🚀 degradation is harder to plan accurately
🚀 degradation makes things even slower for less
powerful clients
🚀 hope for the best, plan for the worst
17. JS apps
🚀 “oh, but this isn’t for people who don’t have
JavaScript”
18. JS apps
🚀 “oh, but this isn’t for people who don’t have
JavaScript”
🚀 “..or people returning to the site”
19. JS apps
🚀 “oh, but this isn’t for people who don’t have
JavaScript”
🚀 “..or people returning to the site”
🚀 “..or people going through a tunnel on a train”
21. have we learned nothing?
🚀 obviously we want the best experience possible
🚀 this is the internet; “possible” changes
🚀 brittle expectations of our users means a worse
experience
🚀 for everyone
23. a method
🚀 build servers as if every client is text-only
🚀 build clients as if the internet will disappear
tomorrow
🚀 expect people not to use the app the way you
expected
24. some simple principles
🚀 state is shared between client and server
🚀 the client takes responsibility for state offline
🚀 and attempts to sync when it comes online
🚀 actions, by default, occur on both client and
server
27. “computer, enhance!”
🚀 is JS available?
🚀 great, run the client-side app
🚀 are we online?
🚀 great, send updates to the server
28. “computer, enhance!”
🚀 is JS available?
🚀 great, run the client-side app
🚀 are we online?
🚀 great, send updates to the server
🚀 client and server aren’t in sync?
🚀 no problem, put them in sync
37. same on the server
app.get( “/register/:step?”,
function( req, res ) {
var step =
req.params.step || registration_step;
res.render(
“reg_step_” + step, registration_data );
});
39. glad you asked!
🚀 updates about state are separate from updates
as a result of state
🚀 data updates are chunked
🚀 state updates are instant (if possible)
44. everything handled
function set_registration_step( step ) {
my_app.state.registration_step = step;
update_state( “registration_step_” + step );
var data = get_data( step - 1 );
save_data( ( step - 1 ), data );
$( “form-step-” + step ).show();
}
45. and stored
function save_data( step, data ) {
db.put( data, “registration_” + step,
function( err, response ) {
if ( err ) {
show_error( “Data not saved!” );
}
});
}
46. note: plan your storage
🚀 small apps may be able to be stored entirely
🚀 only the current workflow in large apps may be
able to be stored
🚀 the more changes happen offline, the less room
for potentially used data
48. how does the client know it’s
offline?
🚀 poll for server updates
🚀 catch a socket error
🚀 sending data via normal XHR
🚀 wait for an error saving
49. hope for the best,
plan for the worst
function update_state( state ) {
state_cache.push( state )
var req = $.ajax({
type: “POST”,
url: “/update_state”,
data: state_cache
}).done( function() {
state_cache = [];
}).fail( function( xhr, err ) {
if ( err == “timeout” ) start_heartbeat();
});
}
51. how do know you’re back?
🚀 try it
🚀 navigator.onLine
🚀 probably some sort of heartbeat
🚀 no matter your strategy
52. and then:
🚀 send client’s current state
🚀 follow a defined path from server’s current state
to client’s
🚀 or send specific steps from the client
🚀 sync server data with client
54. so let’s talk actions
<input type=”submit” value=”Continue ☞” />
<!-- NO NO NO NO NO NO NO NO NO NO NO
<div id=”save-step-1”>Continue ☞</div> -->
55. actions in the interface
🚀 never trigger actions directly
🚀 actions are triggered by state changes
🚀 state changes update client and server
🚀 then the action itself occurs
58. saving (for example)
🚀 fetches user-entered data from the interface
🚀 stores it in an application variable
🚀 adds it to the local data store
🚀 notifies the server that a save occurred
🚀 syncs data
59. ..then the server
🚀 default: fetches user-entered data from the client
🚀 enhanced: syncs with client DB
🚀 stores it in an application variable
🚀 adds it to the backend data store
🚀 notifies any other connected clients that there are
changes
61. other connected clients &
reloads
🚀 fetches user-entered data from the server
🚀 stores it in an application variable
🚀 adds it to its local data store
🚀 updates its interface
62. if JS disappears
🚀 we use HTML behaviors (links, buttons) to skip
right to the server step
🚀 concurrent users receive immediately-expiring
data
63. we need state reflected in:
🚀 client and server variables, ofc
🚀 the URL
🚀 the CSS
64. tada: it’s progressive!
🚀 our server has a version of the app and can
render its interface
🚀 client side can continue without the server, in
case we go offline
🚀 real-time communication and multiple
concurrent users follow the same pattern
65. our app is now
🚀 usable by multiple clients
🚀 in multiple locations
🚀 and easier to continue extending
🚀 by layering functionality, as with progressive
enhancement