Graceful Degradation & Progressive Enhancement
Total Page:16
File Type:pdf, Size:1020Kb
Accessites.org Graceful Degradation & Progressive Enhancement Posted February 6th, 2007 by Tommy Olsson Graceful degradation and progressive enhancement are two sides of the same coin. Both are — in this context — applied to make a web site accessible to any user agent, while providing improved aesthetics and/or usability for more capable browsers. The difference between the two is where you begin your approach. Some time ago I wrote an article here about two very different approaches to web design: Visual Vs. Structural. The concepts of graceful degradation and progressive enhancement relate closely to those two approaches. Graceful Degradation Graceful degradation is the older of the two concepts. The term is also used in fields other than web design, such as fault tolerant mechanical or electrical systems. The premise for graceful degradation is to first build for the latest and greatest, then add handlers for less capable devices. In other words, focus on the majority before catering to those outside the mainstream. This is quite similar to the visual approach of web design, where the first priority is to make it look good to most visitors. A familiar example of graceful degradation is the alt attribute for images. When used properly it provides a text equivalent that conveys the same information as the image to users who cannot perceive the image itself. The text equivalent is most likely not as aesthetically pleasing, and an image can say more than a thousand words, so the user experience is slightly degraded. Using layout tables may be seen as one form of graceful degradation: if the CSS styling cannot be applied — e.g., in really old browsers — at least the basic page layout is retained. But it doesn’t work very well for text browsers like Lynx and some mobile phone browsers, which do not support tables. Another common occurrence in sites built from the graceful degradation point of view is the noscript element. You provide some feature based on JavaScript and add a more basic version for user agents that do not support JavaScript or have client-side scripting disabled. One example could be the ubiquitous drop-down or fly-out menu: Example One <script type="text/javascript" src="/menu.js"></script> <noscript> <ul id="menu"> 1 of 2 <li><a href="/">Home</a></li> <li><a href="/products/">Products</a></li> <li><a href="/services/">Services</a></li> </ul> </noscript> The JavaScript function may create a menu where “Products” and “Services” have sub-menus that drop down, fly out or expand when the user’s mouse pointer hovers over the main items. The non-JavaScript alternative provides immediate access only to the main items, while the sub-menus are (presumably) included in similar noscript elements on the respective interior pages. This approach is designed for mainstream users — those with a graphical browser that supports JavaScript — but it degrades gracefully so that it works in even the humblest of text browsers. It also adds the benefit of proper HTML anchor links to non-JavaScript user agents like search engine spiders, thus negating the critical SEO disadvantage of JavaScript menus. There is one problem with noscript, though. I may use a browser that supports JavaScript and has it enabled, but there could be a company firewall that strips incoming JavaScript for security reasons. In this case the noscript element will not be rendered (because my browser supports scripting) but the JavaScript code that should create the menu won’t be applied either, because it gets stuck behind the firewall. Sorry. Comments are closed. Copyright © 2005-2011, Accessites.org. Some rights reserved 2 of 2 Accessites.org Graceful Degradation & Progressive Enhancement Posted February 6th, 2007 by Tommy Olsson Not-So-Graceful Degradation Unfortunately the concept of graceful degradation sometimes deteriorates way past the point where the adjective “graceful” can reasonably be applied. The worst case is code like this: Example Two <script type="text/javascript" src="/menu.js"></script> <noscript> <p>Please upgrade to a browser that supports JavaScript.</p> </noscript> This is unacceptable for any serious web site, but may be forgiven on a small amateur site whose target audience is friends and family. We, as web designers or developers, have no right to tell our visitors which browser they “should” use; nor do we have the right to tell anyone to “enable JavaScript.” We don’t know why this visitor uses Lynx or why that visitor has disabled client-side scripting. They may not even have the option of “upgrading” or “enabling JavaScript” because of bandwidth, hardware or budget constraints; IT department policy; or because they are not even using their own computer. Graceful degradation is far better than having a site that is completely inaccessible to some users, but it is not the best approach. As with the visual design approach, it is often difficult to begin with all the features and then remove them one by one, without everything falling apart. Progressive Enhancement Progressive enhancement became known — at least under that name — in 2003, when Steve Champeon began using it on Webmonkey and during the SXSW conference. It starts at the opposite end from graceful degradation: begin with the basic version, then add enhancements for those who can handle them. Again, comparing it to the design approaches: this is the same basic thought as in structural design. That starts with the markup and adds styling on top of that, which is progressive enhancement all by itself. The most common occurrence of progressive enhancement is probably the external CSS style sheet. It is ignored by non-CSS browsers — which thus get only the plain markup and render it according to their own built-in style sheets — but it is applied by modern graphical browsers, thus enhancing both the aesthetics and the usability for mainstream and advanced users. 1 of 4 Other examples of progressive enhancement are the various image replacement techniques, the Flash satay method and (sometimes) AJAX. Progressive enhancement when it comes to JavaScript is becoming more common these days. The key to this is known as unobtrusive JavaScript. An unobtrusive script is silently ignored by user agents that do not support it, but is applied by more capable devices. Just like an external style sheet. Let us revisit the navigation menu we examined as an example of graceful degradation. How would that be done using progressive enhancement? We would begin by creating our markup, aiming for the lowest common denominator: HTML. A navigation menu is, semantically, a list of links. The order of those links does not affect the meaning of the list as a whole; thus it is an unordered list. Example Three <ul> <li><a href="/">Home</a></li> <li><a href="/products/">Products</a></li> <li><a href="/services/">Services</a></li> </ul> This will work in everything from Lynx and Mosaic 1.0 to the latest versions of Opera, Firefox and Safari. Googlebot and its arachnoid cousins will also love it. The next step is to add enhancements for the vast majority of users whose browsers support CSS. We add rules in an external CSS file to style the menu. After adding a link element with a reference to the external style sheet, it looks a lot better to most visitors, but it is in no way detrimental to those Lynx users or to the Googlebot. (OK, they’ll have to download a few more bytes, but it will be negligible even on a slow dial-up connection or a GSM mobile phone.) But we can enhance this further by adding drop-down or fly-out or expanding sub-menus to the main items, using unobtrusive JavaScript. To reduce the amount of script code, we begin by assigning ids to the list items: Example Four <li id="products"><a href="/products/">Products</a></li> <li id="services"><a href="/services/">Services</a></li> Then we create a separate JavaScript file with a couple of functions: Example Five function addProducts() { // Find the li element to which we will add a sub-menu var parent = document.getElementById("products"); // Make sure it exists (fail silently) if (parent) { // Create a nested unordered list var ul = parent.appendChild(document.createElement("UL")); // Add the list items and links 2 of 4 var items = [ ["Blue Widgets", "blue"], ["Red Widgets", "red"] ]; for (var i = 0; i < items.length; ++i) { var li = ul.appendChild(document.createElement("LI")); var a = li.appendChild(document.createElement("A")); a.href = "/products/" + items[i][1]; a.appendChild(document.createTextNode(items[i][0])); } } } The addServices() function would look very similar. Then we need to be sure that the browser can handle those DOM functions: Example Six function createSubMenus() { // Make sure that the DOM functions we will use are supported // (fail silently) if (typeof document.getElementById != "undefined" && typeof document.createElement != "undefined" && typeof document.createTextNode != "undefined") { addProducts(); addServices(); } } This function makes sure that the main DOM functions used by addProducts() and addServices() are supported by the browser. If not, the function does nothing; it causes no harm and there will be no JavaScript error message or warning icon. This approach is called object detection and is infinitely better than the old-school browser sniffing scripts that stopped working as soon as a new browser (or a new version of an existing browser) was released. Finally, to get the browser to call those functions as soon as the page has loaded. Example Seven if (window.addEventListener) { window.addEventListener("load", createSubMenus, false); } else if (window.attachEvent) { window.attachEvent("onload", createSubMenus); } else { window.onload = createSubMenus; } Note that this piece of code is not enclosed in a function. It will add an event listener for the window’s “load” event, calling our menu-creating function immediately after the HTML document has finished loading.