Adding CSS rules with JavaScript to hide content on page load

Update: I’ve had to update the code to get it to work reliably in Safari 2. Much thanks to this article on the YUI Blog for another solution.

A lot of ajax-y web pages involve hiding and showing content. My current project has a tons of content related to a video in dynamic reveals that the user can open and close. Of course, accessibility is important, so for users without JavaScript enabled, all of this hidden content should be visible.

Typically, JavaScript would explicitly hide this content as soon as it is available. Something like:

  1. // using prototype.js
  2. document.observe("dom:loaded", function() {
  3.   // initially hide the reveal container
  4.   $('revealContent').hide();
  5. });

I chose prototype.js because of its notorious file size, and I know a lot of you still don’t compress and gzip their JavaScript! If you’re loading a lot of JavaScript, HTML, and CSS, chances are pretty likely that the #revealContent element will display before that that function has a chance to run.

So how do we hide that element before it has a chance to load? The technique I’m using adds a CSS rule to a new stylesheet as soon as possible, i.e. when the JavaScript is interpreted.

  1. /* Add a rule in CSS that lets you override the generated rule */
  2. #revealContent.show {
  3.     display: block;
  4. }
  1. // JavaScript
  2.  
  3. var CSSRules = function() {
  4.  
  5.  var headElement = document.getElementsByTagName("head")[0],
  6.   styleElement = document.createElement("style");
  7.  styleElement.type = "text/css";
  8.  headElement.appendChild(styleElement);
  9.  
  10.  // memoize the browser-dependent add function
  11.  var add = function() {
  12.   // IE doesn't allow you to append text nodes to <style> elements
  13.   if (styleElement.styleSheet) {
  14.    return function(selector, rule) {
  15.     if (styleElement.styleSheet.cssText == '') {
  16.      styleElement.styleSheet.cssText = '';
  17.     }
  18.     styleElement.styleSheet.cssText += selector + " { " + rule + " }";
  19.    }
  20.   } else {
  21.    return function(selector, rule) {
  22.     styleElement.appendChild(document.createTextNode(selector + " { " + rule + " }"));
  23.    }
  24.   }
  25.  }();
  26.  
  27.  return {
  28.   add : add
  29.  }
  30. }();
  31.  
  32. CSSRules.add('#revealContent', 'display: none !important');
  33.  
  34. // later
  35. $('revealContent').addClass('show');

I haven’t tested this exhaustively, but it works as intended in Internet Explorer 6 and 7, Firefox 2 and 3, Safari 2 and 3, and Opera 9. I originally expected some strange behavior from adding elements to the <head> before it finished loading, but fortunately this isn’t the case.

You could also include a stylesheet or a <style> tag in a <noscript> tag, but I prefer to keep all of this behavior in JavaScript.

Update: The old code:

  1. // JavaScript
  2. var CSSRules = function() {
  3.  
  4.     // create a stylesheet
  5.     var stylesheet = document.createElement("style");
  6.     stylesheet.setAttribute("type", "text/css");
  7.     document.getElementsByTagName("head")[0].appendChild(stylesheet);
  8.     stylesheet = document.styleSheets[document.styleSheets.length - 1];
  9.  
  10.     return {
  11.         add: function(selector, rule) {
  12.             // the Microsoft method
  13.             if (stylesheet.addRule) {
  14.                 stylesheet.addRule(selector, rule, 0);
  15.  
  16.             // the standard method
  17.             } else if (stylesheet.insertRule) {
  18.                 // safari 2 apparently ignores this
  19.                 stylesheet.insertRule(selector.concat('{' + rule + '}'), 0);
  20.             }
  21.         }
  22.     };
  23. }();
  24.  
  25. CSSRules.add('#revealContent', 'display: none !important');
  26.  
  27. // later
  28. $('revealContent').addClass('show');

« Developing Silverlight 1.0 RIAs: XAML Developing Silverlight 1.0 RIAs: JavaScript Optimization »

Leave a Reply