Introduction

In my previous feature on CSS layouts, I talked a bit about the different CSS layout approaches available to a designer when building a web site. In this feature, I’d like to focus on a dynamic resolution dependent layout implementation that I think provides a strong alternative for those trying to find a balance between fluid and fixed layouts. With dynamic layouts, the possibilities really are quite endless. You can change 3-column layouts to 2-column layouts, provide appropriate font-sizes to increase legibility, and even reward higher resolution viewers with extra content and larger images. And no, you don’t have to put php in the CSS or lose the caching benefits of using external stylesheets to make it work.

Before I really got into JavaScript, I was always frustrated by how I could never really get a fluid layout to look right across all the different resolutions users were using to see my sites. Columns never really scaled exactly the way I wanted them to and tiny font-sizes on higher resolutions, which were okay on lower resolutions, killed content legibility. When I tried to drop 800x600 layouts, I ended up inconveniencing users that didn’t maximize their browser windows. Until I saw The Man in Blue’s article on Resolution Dependent Layouts, I felt fixed layouts failed to take advantage of the dynamic medium of the web and fluid layouts failed to truly adapt to the variety of viewing methods. While The Man in Blue’s implementation is a great piece of work, I think we can create our own version that’s a bit more modular and easier to develop for both programmers and designers.

See It In Action

I’ve built a quick demo page showcasing how a dynamic resolution dependent layout can offer adaptive alternatives with a very basic XHTML wireframe.

Just resize your browser to see the layout change accordingly. What’s nice about this method is that I don’t have to load a completely new CSS file from scratch for each layout. I only need to load the rules needed to adapt the default layout to the browser width size. It’s not so much a stylesheet switcher as it is a stylesheet adapter.

Implementation

To follow along, download dynamiclayouts.zip, which contains the files used in the demo above.

The first thing we want to do is to place inside the head element all the stylesheets we’re going to be calling on to determine each dynamic layout. Here’s an example set from the demo:

<link rel="stylesheet" type="text/css" href="css/default.css" title="default"/> <link rel="alternate stylesheet" type="text/css" href="css/thin.css" title="thin"/> <link rel="alternate stylesheet" type="text/css" href="css/wide.css" title="wide"/> <link rel="alternate stylesheet" type="text/css" href="css/wider.css" title="wider"/>

Notice that we’ve added title attributes to all of the link elements and designated the dynamic CSS stylesheets as “alternate stylesheets” in the rel attribute. Be sure to indicate your primary CSS stylesheet by setting its title attribute to “default.” The “default” stylesheet is used as the foundation for all of the dynamic layouts and if JavaScript is disabled, this will be the stylesheet that the site will use to display the page. If you use multiple stylesheets to build the default view of your site, you’ll want to title all of them as “default” so the script doesn’t discard them when the layout is dynamically altered. If you want to adapt this method to create a true CSS switcher, just remove the “default” title attribute and the script will disable the foundation stylesheet completely and use only the rules in the alternative stylesheet.

Next, we want to include the dynamiclayout JavaScript file:

<script src="scripts/dynamiclayout.js" type="text/javascript"></script>

Inside the JavaScript take a look at the dynamicLayout function.

function dynamicLayout(){ var browserWidth = getBrowserWidth(); //Load Thin CSS Rules if (browserWidth < 750){ changeLayout("thin"); } //Load Wide CSS Rules if ((browserWidth >= 750) && (browserWidth <= 950)){ changeLayout("wide"); } //Load Wider CSS Rules if (browserWidth > 950){ changeLayout("wider"); } }

The heart of our process is in the browser width detection on the first line and we’re going to use The Man In Blue’s getBrowserWidth() function to help us find it. Check out the code here:

function getBrowserWidth(){ if (window.innerWidth){ return window.innerWidth;} else if (document.documentElement && document.documentElement.clientWidth != 0){ return document.documentElement.clientWidth; } else if (document.body){return document.body.clientWidth;} return 0; }

For the demo, I’ve set it up so the site adapts to 3 different resolution scenarios based on browser width: smaller than 750px, larger than 750px (but smaller than 950px) and larger than 950px. The changeLayout functions that are used in dynamicLayout correspond to the title attributes in our alternative stylesheets. As you can see, it’ll be pretty easy to tweak the if statements to your own needs.

Now, inside of your “alternative stylesheets”, you’ll want to specify the rule changes needed to change your default layout to adapt to that particular resolution scenario. The CSS rules in the alternative stylesheets are applied after the default CSS file is loaded so they’ll override any rules that don’t have !important selectors applied to them. While you could redo every CSS rule in the default stylesheet, often layouts don’t need many changes to make them work in different situations. In the demo, for example, while the “default” stylesheet uses over 10 rules to create the foundation design (some sites use over 100 rules), the “thin” CSS stylesheet need only to change a few rules and selectors to make the site work on small browser widths:

/* ----- Thin CSS Rules ----- */body{ font-size:.8em; } #container{ width:80%; } #primaryContent{ width:100%; line-height: 125px; } #secondaryContent{ width:100%; line-height: 125px; }

This is one of the key strengths to using this method for creating dynamic layouts. We can easily switch from fixed to fluid layouts based on what’s going to be best for the user and each layout is conveniently contained in it’s own external file, so we only need to specify the changes needed to adapt a design for a particular situation. This makes it easier to understand, faster to design (because it reduces CSS redundancy) and better to develop with because it keeps the presentation layer neatly separated from the behavior layer.

To finish up, we’re just use John Resig’s winning addEvent function to run our functions when the page is loaded and when the users resize their browser windows.

//addEvent() by John Resig function addEvent( obj, type, fn ){ if (obj.addEventListener){ obj.addEventListener( type, fn, false ); } else if (obj.attachEvent){ obj["e"+type+fn] = fn; obj[type+fn] = function(){ obj["e"+type+fn]( window.event ); } obj.attachEvent( "on"+type, obj[type+fn] ); } } //Run dynamicLayout function when page loads and when it resizes. addEvent(window, 'load', dynamicLayout); addEvent(window, 'resize', dynamicLayout);

If you feel changing the layout on resize is a bit too jarring, just remove the second addEvent call to dynamicLayout on resize to limit layout adaptation to occurring only when the web page is first loaded or refreshed by the user. One nice thing about using these functions is that you can easily adapt the code with a little cookie magic to create a better stylesheet switcher to provide your users the choice of what layout option they prefer.

Final Thoughts

Before I leave you be, I’d like to take some time to talk about how I think this implementation should have been created. Ideally we should be manipulating the @import CSS rules to choose the appropriate stylesheet to tack on the end of the default CSS file. Unfortunately, W3C’s specifications for dealing with CSS rules in JavaScript are so badly implemented across the browsers that we have to use a hacked CSS stylesheet switcher function based on disabling link elements from 2001 to make dynamic layouts work sanely. C’mon people, help me with the magic making. I honestly believe that proper CSS rule implementation by the browser vendors could create the low barrier of entry needed to get a majority of designers to the next level of web development: DOM manipulation. It would be so much easier to learn how to manipulate the presentation layer, if we could use the same vocabulary for making changes in CSS in JavaScript. For example, how nice would it be to be able to use following:

var defaultSheet = document.styleSheets[0]; defaultSheet.insertRule("#container{width:500px;}");

or

totalRules = defaultSheet.cssRules.length; lastRule = defaultSheet.cssRules[totalRules - 1]; defaultSheet.addImport("/css/wide.css", lastRule);

Yeah, well too bad for us. Most of the syntax for manipulating CSS rules is forked between IE and Gecko browsers and Safari refuses to change the page rendering even though it recognizes the methods. It’s sad to see how this prevents a lot of exciting possibilities and I hope the recent JavaScript renaissance will help people recognize these limitations so they can ask for them to be fixed. If you want to read more about the problems with CSS rule implementation in JavaScript, check out Peter-Paul Koch’s article on the subject. Anyway, I’m off my soap box now. Have fun with dynamic resolution dependent layouts and be sure to keep in touch with your favorite browser vendor about CSS rules!