Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

wrote description

  • Loading branch information...
commit 233c692ff62ee7b54363a72f461aee40998e6ab1 1 parent 6be4e7b
@micheljansen authored
View
4 progressiveapp.rb
@@ -4,7 +4,7 @@
class ProgressiveApp < Sinatra::Base
def simulate_delay
- sleep 1+rand(2)
+ # sleep 1+rand(2)
end
def partial(partial)
@@ -16,7 +16,7 @@ def partial(partial)
end
get '/' do
- erb :index
+ redirect "/pages/introduction"
end
get '/pages/:page' do |page|
View
95 views/_introduction.erb
@@ -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="https://github.com/mtrpcic/pathjs">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>
+
</div>
View
113 views/_progressive.erb
@@ -1,2 +1,111 @@
-<h2>Progressive Enhancement</h2>
-<p>Cool stuff</p>
+<h1>Progressive Enhancement with Path.js</h1>
+<p>
+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="http://en.wikipedia.org/wiki/Single-page_application">Single Page Application</a>
+that updates the content dynamically using AJAX requests.
+</p>
+
+<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="http://www.sinatrarb.com/">Sinatra</a>:
+<pre>
+<%=escape_html <<-eos
+get '/' do
+ redirect "/pages/introduction"
+end
+
+get '/pages/:page' do |page|
+ erb :index, :locals => {:page => page}
+end
+eos
+%></pre>
+
+Additionally, there are three templates:
+<a href="https://github.com/micheljansen/progressive_demo/blob/master/views/index.erb">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="https://github.com/micheljansen/progressive_demo/blob/master/views/_introduction.erb">_introduction.erb</a>
+and
+<a href="https://github.com/micheljansen/progressive_demo/blob/master/views/_progressive.erb">_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>
+<p>
+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.
+</p>
+
+<p>
+This is why we use
+<a href="https://github.com/mtrpcic/pathjs">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="https://github.com/mtrpcic/pathjs/wiki/HTML5-PushState">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.
+</p>
+
+<p>
+The heart of the trick fits in a few lines of JavaScript (simplified):
+
+<pre>
+// Define Route
+Path.map("/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
+$(document).ready(function(){
+ Path.history.listen()
+
+ $("a").live("click", function(event){
+ if(Path.match(url)) {
+ event.preventDefault()
+ Path.history.pushState({}, "", $(this).attr("href"))
+ }
+ });
+})
+</pre>
+
+What this does is catch all links to "/pages/pagename"
+and perform an AJAX request to "/partials/pagename" instead.
+</p>
+
+<p>
+What remains, is adding some code on the server side
+to serve the partial content:
+<pre>
+def partial(partial)
+ erb "_#{partial}".to_sym
+end
+
+get '/partials/:partial' do |page|
+ partial page
+end
+</pre>
+</p>
+
+<p>
+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.
+</p>
View
2  views/index.erb
@@ -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>
</ul>
</div>
</div>
Please sign in to comment.
Something went wrong with that request. Please try again.