Fixing YUI CSS grid issues with JavaScript and the DOM

By Christian Heilmann

Introduction

In the last article I introduced you to the YUI CSS grids and showed how to create a CSS layout by adding the right markup and classes to a basic and valid HTML structure. We ended pointing out some problems of CSS layouts and I hinted that you can use JavaScript to fix those. Well, this is the time and this is the place.

Adding some JavaScript magic

In the last article in this series I've come as far as I can go with CSS and grids (well, I could have covered nesting in more detail, but you can have a go at that yourself and – let's face it, you will use the grids builder like anybody else.) Now it is time to add some JavaScript to work around some layout issues.

JavaScript can do several things CSS cannot do. Specifically JavaScript can read the size of screen elements and the browser, allowing you to fix CSS layout issues, such as inconsistent column heights. The best example are columns with the same height independent of content. Below I'll add some content and styles to the grid we defined earlier. The first issue to notice is that, while the columns and everything work out, they do not line up with each other, and are as high as their content, as shown in Figure 1.

The CSS columns do not line up

Figure 1: The CSS columns by default do not line up, and are as high as their text content.

However, before I can fix that, I'll have to work around another assumption I made.

Will the template fit?

The doc2 template assumes the browser viewport to be at least 950 pixels wide. If that is not the case, the browser will show a horizontal scrollbar, which is not a nice thing to do to a visitor.

I can work around that problem using the YUI DOM utility. This part of the library helps you access the document, read out dimensions of the browser and elements and set styles reliably across browsers. To fix the problem of the horizontal scrollbar, I can do the following using JavaScript:

Check that the viewport is at least 950 pixels wide If it isn't, set the id of the main container to doc (which means switching to the 750 pixel wide layout), and if it is, set it to doc2.

The code is pretty simple (check it out in switchtemplate.html.) At the end of the body I add the combined YUI components Yahoo, DOM and Event and my own script.

<script type="text/javascript" src="http://yui.yahooapis.com/2.3.1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src="switchtemplate.js"></script>

The YUI include automatically gives us the YAHOO.example namespace, which I use to define the code in switchtemplate.js:

YAHOO.example.switchTemplate = function(){
    var outerContainer = YAHOO.util.Dom.get('doc2') || YAHOO.util.Dom.get('doc');
    if(outerContainer){
        var currentWidth = YAHOO.util.Dom.getViewportWidth();
        outerContainer.id = (currentWidth < 950) ? 'doc' : 'doc2';
    };
}();

I am using the JavaScript Module Pattern (as explained in detail on the YUI blog) to make sure that none of our code is in the global variable space.

I then use the YAHOO.util.Dom.get() method to retrieve the outer container element. I check if this is the element with the id doc2; if that one doesn't exist I select the one with the id doc instead. I store the element in the variable outerContainer and test if it has been successfully set.

If it has the YAHOO.util.Dom.getViewportWidth() method reads the width of the current browser window. If the width is less than 950 the id of the outer container is set to doc, otherwise it's set to doc2. The result can be seen in Figure 2:

Using JavaScript to dynamically alter the layout depending on browser width

Figure 2: When the browser is less than 950 pixels wide the template switches to the one expecting at least 750 pixels space.

Fixing the sidebar

That took care my assumption of 950 pixels. The next annoyance is the side bar not being as high as the rest of the content. This can also be easily fixed, and my next example - fixsidebar.html - does exactly that.

The logic is to check the height of the yui-main container and set the height of the sidebar to this one. That way the sidebar will span the whole height regardless of its content. You can find out the height of an element from its offsetHeight property, and this is what I use in fixsidebar.js:

YAHOO.example.fixSideBar = function(){
    var outerContainer = YAHOO.util.Dom.get('doc2') || YAHOO.util.Dom.get('doc');
    if(outerContainer){
        var currentWidth = YAHOO.util.Dom.getViewportWidth();
        outerContainer.id = (currentWidth < 950) ? 'doc' : 'doc2';
    };
    var mainContainer = YAHOO.util.Dom.get('yui-main');
    var sideBar = YAHOO.util.Dom.get('sidebar');
    if(mainContainer && sideBar){
        YAHOO.util.Dom.setStyle(sideBar,'height',mainContainer.offsetHeight + 'px');
    };
}();

This checks for the existence of the elements with the ids of yui-main and sidebar, and sets the height style property to the offsetHeight of the yui-main element. In order to set style properties we use the YAHOO.util.Dom.setStyle() method. Once this script has run the side bar will be as high as the main container and look a lot cleaner, as seen in Figure 3:

Fixing the sidebar using JavaScript

Figure 3: By reading out the height of the main container and setting the height of the side bar we can make it span all the way down to the footer.

Normalizing column heights

The last little hiccup in my design is that the headings and content boxes in the three column grid are all a different height. This can be fixed using the same trick I used for the sidebar. The only difference is that I need to loop through all of them to determine which one is the highest. The following script (found in fixcolumns.html) shows this solution:

YAHOO.example.fixColumns = function(){
    var outerContainer = YAHOO.util.Dom.get('doc2') || YAHOO.util.Dom.get('doc');
    if(outerContainer){
        var currentWidth = YAHOO.util.Dom.getViewportWidth();
        outerContainer.id = (currentWidth < 950) ? 'doc' : 'doc2';
    };
    var mainContainer = YAHOO.util.Dom.get('yui-main');
    var sideBar = YAHOO.util.Dom.get('sidebar');
    var showCase = YAHOO.util.Dom.get('showcase');
    if(showCase){
        function setMax(collection){
            var max = 0;
            for(var i=0;collection[i];i++){
                var height = collection[i].offsetHeight;
                max = height > max ? height : max;
            };
            for(i=0;collection[i];i++){
                YAHOO.util.Dom.setStyle(collection[i],'height',max + 'px');
            };            
        };
        setMax(showCase.getElementsByTagName('h2'));
        setMax(showCase.getElementsByTagName('p'));
    };
    if(mainContainer && sideBar){
        YAHOO.util.Dom.setStyle(sideBar,'height',mainContainer.offsetHeight + 'px');
    };
}();

Note that this functionality needs to be added before the one fixing the sidebar as the overall height of the container might change.

I check if the element with the id of showcase exists and define a function that determines the highest element in a collection. The function does this by reading the offsetHeight of each and setting a max variable to the highest. It then loops through all the elements and sets their height to this max value.

This function is called twice, once for all the h2 elements in the element with an id of showcase and a second time with all the p elements. Once this has run my layout looks clean and aligns nicely, as shown in Figure 4:

The main 3 columns are now fixed as well

Figure 4: I can fix the height of columns by determining which one is the highest and setting the height of all the others accordingly.

To be continued

These are but a few small examples how you can use the DOM component of the YUI to fix layout issues that aren't covered by the YUI grids. In my next article I'll look more closely into the DOM and the event parts of the YUI and go on towards other YUI components like animation and Ajax.

This article is licensed under a Creative Commons Attribution, Non Commercial - Share Alike 2.5 license.

Comments

The forum archive of this article is still available on My Opera.

No new comments accepted.