Browse files

wrote description

  • Loading branch information...
1 parent 6be4e7b commit 233c692ff62ee7b54363a72f461aee40998e6ab1 @micheljansen committed Dec 8, 2011
Showing with 204 additions and 10 deletions.
  1. +2 −2 progressiveapp.rb
  2. +90 −5 views/_introduction.erb
  3. +111 −2 views/_progressive.erb
  4. +1 −1 views/index.erb
@@ -4,7 +4,7 @@
class ProgressiveApp < Sinatra::Base
def simulate_delay
- sleep 1+rand(2)
+ # sleep 1+rand(2)
def partial(partial)
@@ -16,7 +16,7 @@ def partial(partial)
get '/' do
- erb :index
+ redirect "/pages/introduction"
get '/pages/:page' do |page|
@@ -1,7 +1,92 @@
<div id="introduction">
- <h2>Introduction</h2>
- <p>This is the introduction</p>
- <a href="http://localhost:9292/pages/progressive">link 1</a>
- <a href="/pages/progressive">link 2</a>
- <a href="progressive">link 3</a>
+ <h1>Progressive Enhancement Demo</h1>
+ <p>
+ Progressive Enhancement is a strategy for building websites and webapps
+ that are built around the assumption that some user agents or browsers suck,
+ but they should still be able to access the page,
+ while more capable browsers should be able to benefit from the latest gimmicks.
+ The idea is simple: rather than start designing for fanciest browsers
+ and work your way down plugging holes where the "lesser" browsers fail
+ (so-called "graceful degradation"),
+ you start simple and add the fancier features on top.
+ </p>
+ <h2>JavaScript</h2>
+ <p>
+ One of the biggest challenges in designing for progressive enhancement
+ is JavaScript, or rather the lack thereof.
+ JavaScript is incredibly useful and although almost all browsers
+ have very decent support for it,
+ there is still a slight chance
+ that your visitors are surfing without JavaScript.
+ Either their browser does not support it,
+ or they turned it off for whatever reason<sup><a href="#footnote-1" id="r1">1</a></sup>.
+ It may not even be their fault that JavaScript is not working;
+ maybe you messed up and introduced a bug
+ that causes JavaScript execution to halt.
+ </p>
+ <p>
+ Designing without JavaScript ruins all the fun.
+ It means no AJAX, no cool jQuery animation tricks
+ and no dynamic updates.
+ These things are all very useful and we still want to be able to use them
+ for the majority of users who <em>do</em> have a decent browser.
+ </p>
+ <h2>AJAX</h2>
+ <p>
+ For dynamic AJAX updates, this specifically means
+ that anything that triggers a dynamic update has a static counterpart.
+ For example, a button that submits a form posts to a real form
+ that shows the results.
+ A link that refreshes part of the page,
+ points to a valid URL with the update of that page.
+ </p>
+ <p>
+ For example, we don't do this:<br/>
+ <code><%=escape_html '<a href="#" onClick="fancyAjaxUpdate()"/>click me</a>' %></code>.
+ The link leads nowhere and not having JavaScript
+ (or the fancyAjaxUpdate() method being broken) results in a dead site.
+ </p>
+ <p>
+ Much nicer would be:<br/>
+ <code><%=escape_html '<a href="mypage.html" onClick="fancyAjaxLoad(\'mypage\'); return false">my page</a>' %></code>.
+ This way, we have a valid link to the page, but if we have JavaScript
+ we load it dynamically instead.
+ This has one last problem in that it breaks the browser history,
+ resulting in (among other things)
+ non-working back buttons and a lot of confusion;
+ however, this can be fixed with the history API introduced in HTML5.
+ </p>
+ <p>
+ Unfortunately, it is a pain in the ass to design like this.
+ If we are unlucky, it requires us to do all the work double:
+ first we need make sure that we have a valid "mypage.html"
+ and then we need to put just the content elsewhere,
+ so we can dynamically retrieve that using AJAX.
+ Then we need to deal with the browser history
+ and it all just becomes too messy and we give up.
+ </p>
+ <h2>A better solution</h2>
+ <p>
+ This little proof of concept shows that it can be easy
+ to have your cake and eat it.
+ Using <a href="">Path.js</a>,
+ a little jQuery and a wafer thin Sinatra backend
+ (which could also be Ruby on Rails, PHP or anything with a decent template
+ engine and router),
+ we have a page that works identically whether it is accessed
+ with or without JavaScript (except it is much snappier with JavaScript).<br/>
+ To read on, go to <a href="/pages/progressive">the next page</a>.
+ </p>
+ <aside>
+ <ul style="list-style-type: none">
+ <li><a id="footnote-1" href="#r1">[1]</a>
+ You might not even want to assume a user is even using a browser.
+ Maybe they are using cURL or wget,
+ or maybe they are Googlebot or some other web crawler.</li>
+ </aside>
@@ -1,2 +1,111 @@
-<h2>Progressive Enhancement</h2>
-<p>Cool stuff</p>
+<h1>Progressive Enhancement with Path.js</h1>
+This little demo uses Path.js and jQuery to progressively enhance
+the navigation on this minisite.
+Without JavaScript, the site consists of two pages:
+<a href="/pages/introduction">introduction</a> and
+<a href="/pages/progressive">progressive</a>.
+With JavaScript, the site turns into a
+<a href="">Single Page Application</a>
+that updates the content dynamically using AJAX requests.
+<h2>The baseline: everything serverside</h2>
+For browsers without JavaScript, we generate the two pages on the server.
+This is easily done with a few lines of code in <a href="">Sinatra</a>:
+<%=escape_html <<-eos
+get '/' do
+ redirect "/pages/introduction"
+get '/pages/:page' do |page|
+ erb :index, :locals => {:page => page}
+Additionally, there are three templates:
+<a href="">index.erb</a>,
+which contains the general structure of the page, with the navigation bar etc.
+and two partial templates that contain just the content that is unique to that page:
+<a href="">_introduction.erb</a>
+<a href="">_progressive.erb</a>.
+The index.erb template is passed the name of the page,
+so it knows which partial template to use to render the content.
+<h2>Making it dynamic: Path.js</h2>
+Now that we have a working baseline, we can progressively enhance that
+using AJAX to fetch just the content we want and smoothly replace that on the page.
+As explained in the <a href="/pages/introduction">introduction</a>,
+this is hard to do without messing up the browser history and URL bar.
+This is why we use
+<a href="">Path.js</a>;
+a very simple JavaScript routing library.
+At the time of writing it is under 200 lines of code.
+It allows you to map URLs to actions
+that you can act on in JavaScript.
+Path.js even supports the
+<a href="">HTML5 History API via PushState</a>
+so it is able to maintain a consistent state of the address bar while keeping
+back button functionality intact.
+The heart of the trick fits in a few lines of JavaScript (simplified):
+// Define Route"/pages/:page").to(function(){
+ $("#content").html("loading...");
+ $.ajax("/partials/"+this.params["page"])
+ .done(function(data) {
+ $("#content").html(data)
+ })
+// Wire links to go through Path using jQuery
+ Path.history.listen()
+ $("a").live("click", function(event){
+ if(Path.match(url)) {
+ event.preventDefault()
+ Path.history.pushState({}, "", $(this).attr("href"))
+ }
+ });
+What this does is catch all links to "/pages/pagename"
+and perform an AJAX request to "/partials/pagename" instead.
+What remains, is adding some code on the server side
+to serve the partial content:
+def partial(partial)
+ erb "_#{partial}".to_sym
+get '/partials/:partial' do |page|
+ partial page
+That's all!
+Using JavaScript, we can now add some nice transitions, animations etc.
+but the meat of the work is done.
+The same templates are used whether the page is loaded from <code>index.erb</code>
+or served up as a partial via AJAX, so there is no duplication of code.
+For bonus points, you can also use layouts.
@@ -71,7 +71,7 @@
<a class="brand" href="/">Progressive Demo</a>
<ul class="nav">
<li class="introduction"><a href="/pages/introduction">Introduction</a></li>
- <li class="progressive"><a href="/pages/progressive">Progressive Enhancement</a></li>
+ <li class="progressive"><a href="/pages/progressive">Implementation</a></li>

0 comments on commit 233c692

Please sign in to comment.