Mike Nichols - Son of Nun Technology

Holy Grailing Redux - The African Swallows of Equal Columns Using Javascript/Prototype

On my current Monorail project I am using the Holy Grail layout I mentioned here . The javascript I modified for this worked just fine but I wanted to be able to keep an element in the bottom right corner of almost every page showing a dynamic contact email.

My first thought was to place a negative margin-top on the footer, nudging the floated element into the content area...kind of an illusion for placement. The problem is, the script places the columns over the area even with z-indices set.

So, I needed to attach an event to my CSSColumns javascript so that I could use the calculated 'height' to apply to the 'top' style attribute of my absolutely positioned element (nested in a relative element).

Here's the content markup snippet:

<!--BEGIN #content -->
                <div id="content">                    
                        
                    <!--BEGIN #c1-->
                        <div id="c1" class="column">
                            ${ChildOutput}
                        <!--END #c1-->
                        </div>
                        
                        <!--BEGIN #c2-->
                        <div id="c2" class="column">   
                            ${?imageboxout}
                            ${?leftcontent}
                        <!--END #c2-->
                        </div>                        
                        <!--BEGIN #c3-->
                        <div id="c3" class="column"><!--position:relative;-->
                           ${?rightcontent}                            
                            <div id="bottom-right"><!--position:absolute;-->
                                ${?bottomright}
                            </div>
                        <!--END #c3-->
                        </div>                 
                 <!--END #content-->

 

One interesting problem: I was using variable length arguments to pass to my object accepting any number of element ids to modify at pageLoad. But I wanted to use an inline {option:value} syntax. How will this work since I am not passing in a 'options' parameter? The code below shows my solution. Works great and is extensible for future options (unlike the previous incarnation):

 

/*Adapted from http://www.thewatchmakerproject.com/journal/308/equal-height-boxes-with-javascript
fixing IE7 compatibility issues and using prototype.js loaded first*/

/*COMMON USAGE:
    In the head element...
    
        <script type="text/javascript">    
            var setBottomRightDiv = function(maxHeight){
                var h = (maxHeight - 60).toString();
                $('bottom-right').setStyle({'top': h + "px"});
            };
            var col = new CSSColumns('content','c1','c2','c3',{onColumnsSet:setBottomRightDiv});
            
            FastInit.addOnLoad(function() { col.equalise(); });                              

        </script>
    >>Where 'bottom-right' might be an element you want to position based on the maxHeight calculation.
    >>'content','c1','c2','c3' are column (div) container ids. Any number of column ids may be passed in.
    >>The last argument may be for 'options'. Here, the onColumnsSet delegate to fire upon setting.
    >>attach CSSColumns 'equalise' method to the page's onload event. (Here, using FastInit).
*/
CSSColumns = Class.create();
Object.extend(CSSColumns.prototype,{    
    maxHeight: 0,
    elIds: new Array(),
    els: new Array(),
    initialize: function(){
        for(var i=0;i<arguments.length;i++)
        {
            if(!(arguments[i] instanceof Object)){
                this.elIds.push(arguments[i]);
            }
            else{
                this.options = { 
                    onColumnsSet: Prototype.emptyFunction
                };
                Object.extend(this.options,arguments[i] || {});           
            }
            
        }               
    },      
    setEls: function(){        
        for (var i=0;i<this.elIds.length;i++) if (!$(this.elIds[i])) return;
        
        for(var i=0;i<this.elIds.length;i++)
        {
            this.els.push($(this.elIds[i]));
        }       
    },
    setOptions: function(options){
         
    },     
    equalise: function(){
        this.setEls();
        this.maxHeight = this.calcMaxHeight();
        for(var i=0;i<this.els.length;i++){
            this.els[i].style.height = this.maxHeight + "px";            
            
        }
        this.notify('onColumnsSet',this.maxHeight);        
    },    
    calcMaxHeight: function(){
        var h = 0;
        for(var i=0;i<this.els.length;i++)
        {
            if(this.els[i].getHeight()>h)
            {
                h=this.els[i].getHeight();
            }
        }
        return h;
    },  
    notify: function(event_name){
        if(this.options){
            (this.options[event_name])
                return [this.options[event_name].apply(this.options[event_name],$A(arguments).slice(1))];
        }
    }
});

if(typeof(Object.Event) != 'undefined')
    Object.Event.extend(CSSColumns);

Now my element ('bottom-right') is found just where it should be on every page!