How to hide and show initial content, depending on whether JavaScript support is available

Many people ask me how I choose to address an situation where all content in a web should be available without JavaScript, but certain parts hidden if JavaScript is enabled.

It’s a pretty common scenario where, for instance, part of a tree navigation or different blocks in the web page are hidden for JavaScript-enabled web browsers, thus letting the end user toggle them. However, if the end user doesn’t have JavaScript enabled or if it fails for any reason, you would want that toggled content to be visible immediately.

Using noscript?

Just use the noscript element, I guess you’re thinking. While that definitely works to some degree, there are some issues with it. To begin with, content within the noscript element will only be visible to user with JavaScript disabled. Not applicable if you want the content visible to everyone, and then having the interaction presentation enhanced with JavaScript.

Second, noscript only tells you if JavaScript is enabled or not in the web browser; not if it actually works. There are proxy servers out there cleaning out JavaScript files with what it thinks is inappropriate content, over-zealous antivirus programs, firewalls preventing certain functionality, and so on.

In my pragmatic mind, this pretty much renders noscript useless, if you want to be a 100% sure.

Server-side solution?

Not really. You can check USER_AGENT headers and hope for luck, but it’s not a guarantee nor offering a consistent way. What you can do is to add some small JavaScript in your web page, which redirects the page or sends a form or XMLHTTPRequest to the server, which in turn stores the value in a cookie or session variable. Then you can reference that on the server-side for consecutive calls.

This adds up to extra JavaScript and server-side functionality, which, to me, sounds like making it overly complicated for JavaScript detection.

Hiding of elements

I think the most popular way nowadays is to run JavaScript code at the bottom of a web page, and through a JavaScript library’s way to determine if the DOM has finished loading or just by using inline JavaScript code, make a call to hide content which should only be initially visible to the end users with JavaScript disabled.

Examples:

The downside

The problem with this approach, however, is that between the point of your HTML that is to be hidden, and the point where your JavaScript library has been completely included and then found the element(s) to hide, there is a possible discrepancy which will result in “jumping” pages; i.e. you will get a flicker temporarily showing the content, and then hiding it. Not acceptable.

Looking at the above sample code, this behavior occurs for both DOMAssistant, jQuery and Prototype in, amongst others, web browsers like Internet Explorer 6. The only solution where it doesn’t consistently jump is when the code is directly inline, but it does seem to occur the first time that page is loaded in a web browser as well.

A little disclaimer and explanation

The work amongst JavaScript libraries to detect when the DOM has actually loaded is indeed implemented in a solid way. The problem above is the inclusion of the JavaScript file later in the code than the HTML that is to be hidden. If we were to move up the inclusion of the JavaScript file to the top of the document to the head section, no jumping would take place.

Without going into the discussion Where To Include JavaScript Files In A Document, the point here is to show how just one extra HTTP request will slow down the perceived rendering. Imagine a more real page where you have image inclusion, other dependencies making their requests, or even horrible ad scripts using document.write creating iframes, who in turn load some external content (a reality for way to many people).

With such scenarios, there is no rock solid way to prevent certain content from jumping, no matter how optimized your JavaScript code is to check if the DOM has loaded.

A proposed solution

Working back and forth with this, I was looking for a solution that is completely consistent and reliable, while still offering code separation and caching between pages in a web site. What I came up with, if I may say so myself, is quite simple and clever. :-)

Using JavaScript to include another style sheet

The solution is to include a JavaScript file in the head part of the document. If JavaScript is enabled, it directly runs an anonymous function that in turn creates a link element which only contains CSS code to hide chosen elements if JavaScript is enabled.

The way web browsers work, is that JavaScript files has to completely load before any further parsing will take place. This means that when the parsing of the actual content within the body element takes place, your CSS has already been included, hence it’s as if the dynamically applied style has always been there.

And, the beauty of all this is that by following some of the advice presented in Improve Your Web Site Performance – Tips & Tricks To Get A Good YSlow Rating, is that if you use Expires headers in a proper manner, there will only be one request, site-wide, for your JavaScript and CSS files taking care of your problem. From then on, it reads it directly from your local web browser cache.

The code

I’ve put together a little demo page to show JavaScript inclusion of a CSS file. Breaking down the code, here are the vital parts:

First, include the JavaScript file:

<head>
	<title>Using a script to include a CSS file</title>
	<script type="text/javascript" src="script-styles.js"></script>
</head>

This is the anonymous function running in its own scope, preventing clashes with other variable names. Just change the path to the CSS file to whatever suits you:

(function () {
	var head = document.getElementsByTagName("head")[0];
	if (head) {
		var scriptStyles = document.createElement("link");
		scriptStyles.rel = "stylesheet";
		scriptStyles.type = "text/css";
		scriptStyles.href = "script-styles.css";
		head.appendChild(scriptStyles);
	}
}());

And, lastly, create the script-styles.css file and add the necessary CSS for the version of the web site intended for end users with JavaScript enabled:

#hide-with-script {
	display: none;
}

No more jumping!

With this solution, no matter how many other dependencies of other external files you might have in your web page/site, I hope to have offered you a way to hide extra content for people with JavaScript-enabled web browsers, while offering all of the content to those with JavaScript disabled.

Posted in CSS,Developing,DOMAssistant,JavaScript,Technology |

Leave a Reply

Your email address will not be published. Required fields are marked *