In the Smashing Magazine, Heydon Pickering has recently published an article called Reimagining Single-Page Applications With Progressive Enhancement. I’d like to comment on this article and take it as an opportunity to reason about progressive enhancement in general.
Switching between parts of a page
The recipe is:
- First you take a plain HTML page. The HTML may be rendered on the server dynamically.
- The content is divided into different parts (sections). These parts have IDs so they are addressable in the URL, e.g.
- The page contains lots of content, but only one part is visible at once. (Note: the article uses the high-level term “view” here. I’ll stick to “part of the page” here since it’s less ambiguous.)
Another part gets visible when the user follows a link to it:
<a href="#part1">…</a>. Using the CSS
:targetselector, only the requested part is made visible.
Therefore, linking to parts is easy. Also, switching between the parts is fast and accessible.
A sophisticated tab control, not a single-page app yet
So what did we achieve with these steps? We have built a sophisticated tab control. Granted, this is not a normal tab control that is responsible just for a few sections of the page. Here it manages all content on the page. Still the purpose is to show and hide static, server-rendered parts of the page in an accessible way.
header('Location: http://my-app-thing.com#submission-form'). You could also handle query parameters, using URLs like
What is a single-page application anyway?
Let’s take a short detour to find out what single-page apps are about.
Even the original article from 2005 that coined the “Ajax” moniker might help to understand what single-page apps today are about. According to this definition, Ajax is not merely a front-end technique, not just a fancier name for
XMLHttpRequest. It’s a user interface concept for dynamic web sites. The user performs tasks ideally without delays or interruptions, without noticing that server work is being done. Server requests are sent asynchronously in the background.
Single-page applications: A working definition
For now, let’s take this working definition by yours truly:
Sometimes the HTML is rendered on the client, but not necessarily. All HTML code or at least the initial structure may as well be rendered on the server.
Instead of making server round-trips after each user input, the app makes HTTP server requests in the background to publish actions or to load new data. Sometimes the Websockets protocol is used to stream data without the HTTP overhead. Usually the exchange data format is JSON, so the app might talk to generic HTTP REST APIs. But it’s also common to request HTML snippets from the server.
Usually the content is tailored to the user, so there is some user authentication and access control involved.
Usually single-page apps set the URL according to the application state and, vice versa, route a given URL to a specific part of the application, restoring the state. Thereby parts of an app are addressable, just like traditional web pages.
Simple CRUD example apps
For Chaplin.js, an application architecture based on Backbone.js, I have created the cars example app (source code) some while ago. It implements a simple Create, Read, Update, Delete (CRUD) workflow for a list of records. Most Backbone & Chaplin apps have such a workflow somewhere. Compared to TodoMVC, this example has more single-page app features like REST-style routes, module loading, arbitrary HTML templates and controlled data sharing.
A progressively-enhanced todo list
After this detour, I’d like to examine how a progressively-enhanced TodoMVC implementation would look like. What parts are necessary?
- Rendering the todo list on the client and on the server.
- Persisting the todos on the server. Ideally, the list is persisted on the client as well so the app also works offline and synchronizes with the server once the connection is re-established.
As far as I can see, these features fit hardly into the structure the Smashing Mag article describes. Todos can be created and removed arbitrarily. A todo needs to have a unique ID, it needs to be addressable. So we need to transcend the model of having fixed parts of a single page that are just shown and hidden.
hashchange is the easiest way to implement client-side routing, the community has already moved away from such stopgaps.
Coherence and maintenance
Worst of all, you now have state and behavior for the same task—UI rendering—implemented in two languages and running on two computers separated by a high-latency network. Congratulations, you’ve just signed up to solve one of the hardest problems in distributed computing.
Please read this quote a second time because there is plenty of experience condensed in few sentences.
This is still a work in progress. I’m mostly exploring ways to share server and client logic. Having worked with React and Redux on several large client projects before, I’m convinced that this is a solid infrastructure for building progressively-enhanced web apps.
Please see my other articles on the topic:
For a full list of references, see my bookmarks tagged with progressive enhancement.