Progressive Enhancement Techniques 3: the JavaScript

Share this article

web layers
In my previous articles we built a tab control using Plain Old Semantic HTML with unobtrusive CSS styling. This is the final article in a three-part series that illustrates how to build a simple tabbed box using progressive enhancement techniques.

Shaky Scripting

JavaScript is the most risky web page layer. HTML support is guaranteed, CSS is likely, but JavaScript may be unavailable or disabled. Whether that affects your site will depend on your audience but, in general, you can expect 5% of users to not have scripting enabled. That’s a large percentage — certainly higher than the market share of Safari, Chrome or Opera. There are also differences in browser implementations. Fortunately, JavaScript is a versatile language and we can fix compatibility issues. For example, IE5 does not provide the Array push() method, but we can detect when it’s missing and add it ourselves:

if (!Array.prototype.push) {
	Array.prototype.push = function(item) { this[this.length] = item; };
}
Note: adding new properties or methods to native objects is a little risky, but that’s a topic for another article. Many JavaScript frameworks provide functions which overcome browser inconsistencies. The tab box control code shown below is built as a jQuery plugin. It provides a reusable component that can be implemented on any page and jQuery will help with the more mundane aspects of client-side coding, such as DOM manipulation and event delegation. You should be aware that jQuery and other libraries offer limited browser support. Although our tab box could work in IE5 and IE5.5, jQuery does not support those browsers and scripting will fail. It’s not a major problem in this example because the tab box operates without JavaScript. However, you should always check your requirements against your chosen library’s limitations before using it. Finally, jQuery aficionados will know that tabs are already supported in the UI API and many other pre-built controls are available. Most will be better than this example — the code is purposely kept short to demonstrate progressive enhancement techniques.

JavaScript-enabled CSS

Without JavaScript, our tab box uses a scrolling content section. Our script must remove the scrollbars, but we can still specify this using JavaScript-controlled CSS:

/* tab control CSS: JavaScript progressive enhancements */
.tabcontent.active
{
	overflow: hidden;
}
The plugin will therefore apply a class of “active” to the outer content div when the tab box is initialized.

Developing the jQuery TabControl Plugin

The full code is available in jquery.tabcontrol.js
. When the page has loaded, the JavaScript looks for any tag with a class of “tabs” and creates a new instance of a TabControl object for each node found. The node is assigned a TabControlInitialized property to prevent two or more objects pointing at the same HTML code. Two important variables are initialized:
  • (this.) Tab property will contain the nodes of all tabs and their associated content
  • (this.) Active property stores the currently highlighted tab.

// jQuery plugin definition
$.fn.TabControl = function() {

	// tab control object
	function TabControl(list) {

		if (list.TabControlInitialized) return;
		else list.TabControlInitialized = true;

		this.List = list;
		this.Tab = {};
		this.Active = null;
In the next segment, the value of T is set to this (the current object instance) so it can be referenced within the following loop. Each tab is identified and the ID it links to is extracted (see the LinkId method below). The tab and content nodes are then stored in the Tab object:

		var T = this;

		// find and initialize all tabs
		$("li a[href^=#]", list).each(function() {

			var id = T.LinkId(this);
			var content = $("#"+id);
			content = (content.length > 0 ? content[0] : null);

			// link/content object
			T.Tab[id] = {
				link: this,
				content: content
			};
The final part of the loop performs the following operations:
  1. A class of “active” is added to the content’s container (the div with the class “tabcontent”). This will remove the scroll bars, as described above.
  2. A click event handler is applied to all links that reference the tab content, including the tab itself (see the TabSwitch method below).
  3. All tabs and content are made inactive (see the Activate method below).
  4. The currently active tab is determined. This will normally be the first tab unless another ID is specified in the page URL.
The active tab is then activated:

			// set content holder class
			if (content !== null) $(content.parentNode).addClass("active");

			// event delegation
			$("a[href=#"+id+"]").click(function(e) { T.TabSwitch(e) });

			// deactivate tab
			T.Activate(id, false);

			// is tab active?
			if (T.Active === null || "#"+id == location.hash) T.Active = id;

		});

		// show active tab/content
		this.Activate(this.Active);

	}
Finally, we have a three methods. The first, LinkId, returns the in-page ID extracted from a link’s href attribute (the “#” is also removed):

	// returns linked ID
	TabControl.prototype.LinkId = function(link) {
		return link.href.replace(/.*#(.+)$/, "$1");
	};
The TabSwitch method is called when a tab (or any link pointing at the tab’s content) is clicked. It stops the browser’s default action, deactivates the old tab, and reactivates the new one. The final few lines smoothly scroll the page to the tab box if it’s off the screen when a link to tabbed content is clicked. The link jumped to the content location in normal HTML, but our code has cancelled that event and replaced it with a nicer effect.

	// tab click event handler
	TabControl.prototype.TabSwitch = function(e) {

		e.preventDefault();

		var id = this.LinkId(e.target);
		if (id != this.Active) {

			// hide old tab
			this.Activate(this.Active, false);

			// switch to new tab
			this.Active = id;
			this.Activate(this.Active);

		}

		// scroll to tab box if required
		var html = $('html,body');
		var lt = $(this.List).offset().top, lh = $(this.List).height();
		var st = Math.max(html.scrollTop(), $("body").scrollTop()), wh = $(window).height();
		if (lt < st || lt+lh > st+wh) html.animate({scrollTop: lt}, 500);

		return false;
	};
Finally, the Activate method takes two arguments; the tab ID and either true or false to activate or deactivate the tab and its content:

	// activate or deactivate a tab
	TabControl.prototype.Activate = function(id, show) {

		if (this.Tab[id]) {
			if (show !== false) {
				$(this.Tab[id].link).addClass("active");
				if (this.Tab[id].content) this.Tab[id].content.style.display = "block";
			}
			else {
				$(this.Tab[id].link).removeClass("active");
				if (this.Tab[id].content) this.Tab[id].content.style.display = "none";
			}
		}

	};
Our progressively-enhanced tab box is now complete — view the example page. It should work in all browsers even if CSS, JavaScript or both are disabled or fail. There is little need to publish a browser support list
! HTML, CSS and JS screenshot Resource links: Other parts in this series: Related reading:

Frequently Asked Questions about Progressive Enhancement

What is the main difference between progressive enhancement and graceful degradation?

Progressive enhancement and graceful degradation are two methodologies used in web design. The main difference between them lies in their approach. Progressive enhancement starts with a basic, functional website and then adds more complex features for browsers that can handle them. On the other hand, graceful degradation starts with a full-featured site and then removes features for older browsers to ensure the site still functions.

How does progressive enhancement improve accessibility?

Progressive enhancement improves accessibility by ensuring that the basic content and functionality of a website are accessible to all users, regardless of the capabilities of their device or browser. This approach allows users with older browsers, slow internet connections, or disabilities to still access the essential content and functionality of a website.

Is progressive enhancement still relevant today?

Yes, progressive enhancement is still relevant today. With the increasing variety of devices and browsers used to access the web, it’s more important than ever to ensure that websites are accessible and functional for all users. Progressive enhancement allows developers to build websites that work for everyone, while still taking advantage of the latest web technologies for users with modern browsers.

How does progressive enhancement affect website performance?

Progressive enhancement can significantly improve website performance. By starting with a basic, functional website and only adding additional features for browsers that can handle them, progressive enhancement ensures that users only download the resources they need. This can result in faster load times and a better user experience, especially for users with slow internet connections or older devices.

What are some examples of progressive enhancement in action?

Examples of progressive enhancement in action can be seen in many modern websites. For instance, a website might start with a basic HTML structure that includes all the essential content and functionality. Then, CSS is added to enhance the visual appearance of the site for browsers that support it. Finally, JavaScript is added to provide additional interactivity for browsers that can handle it.

How does progressive enhancement relate to responsive web design?

Progressive enhancement and responsive web design are two methodologies that can work together to create websites that are accessible and functional on a wide range of devices and browsers. While responsive web design focuses on adjusting the layout of a website based on the screen size, progressive enhancement focuses on providing a basic level of functionality for all users and then adding more complex features for browsers that can handle them.

What are the challenges of implementing progressive enhancement?

Implementing progressive enhancement can be more time-consuming and complex than building a website with all features from the start. It requires careful planning and testing to ensure that the basic version of the website is functional and that additional features degrade gracefully on older browsers. However, the benefits of improved accessibility and performance often outweigh these challenges.

How can I test if my website is using progressive enhancement?

You can test if your website is using progressive enhancement by accessing it with different devices and browsers, including older ones. The basic content and functionality of your website should be accessible to all users, regardless of the capabilities of their device or browser. You can also use tools like Google’s Lighthouse to analyze your website’s performance and accessibility.

Can progressive enhancement be used with any web technology?

Yes, progressive enhancement can be used with any web technology. It’s a methodology that can be applied to any aspect of web development, from HTML and CSS to JavaScript and beyond. The key is to start with a basic, functional website and then add more complex features for browsers that can handle them.

What are some resources to learn more about progressive enhancement?

There are many resources available to learn more about progressive enhancement. Some recommended ones include the Mozilla Developer Network (MDN), Smashing Magazine, and freeCodeCamp. These sites offer in-depth articles and tutorials on progressive enhancement and other web development topics.

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

javascriptstandardsTutorials
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form