03 February 2011

Menu on multiple lines - Cross Browser


Hello,

Sometimes the menu which you need to display is generated using a repeater and you do not know exactly how many items will be displayed, and here you need a menu which will wrap in case there is not enough space.

I have also searched for this kind of menu on the internet but I could not find one. This is why I decided to create one by myself.

Well of course some ideas are take from some samples but in general I had to rewrite some code and change CSS, in order to make it to work how I needed and cross browser.

The height of the menu items are automatically readjusted to be the same. This is done because otherwise the menu will not be displayed correctly.



Also I bet there is no menu which will work on all browsers, inculding Internet Explorer 6.

Here is the Javascript needed, please note jQuery Core and jQuery UI is needed.



var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;

// open hidden layer
function mopen(element, id) {
    // cancel close timer
    mcancelclosetime();

    // close old layer
    mclose();

    // get new layer and show it
    ddmenuitem = document.getElementById(id);
    $("#IE6Fix").height($(ddmenuitem).height());
    $("#IE6Fix").width($(ddmenuitem).width() + 2);

    $("#IE6Fix").show();
    $(ddmenuitem).show();

    $("#IE6Fix").position({
        my: "left top",
        at: "left bottom",
        of: $(element),
        collision: "fit"
    });

    $(ddmenuitem).position({
        my: "left top",
        at: "left bottom",
        of: $(element),
        collision: "fit"
    });
}

// close showed layer
function mclose() {
    if (ddmenuitem) {
        $("#IE6Fix").hide();
        $(ddmenuitem).hide();
    }
}

// go close timer
function mclosetime() {
    closetimer = window.setTimeout(mclose, timeout);
}

// cancel close timer
function mcancelclosetime() {
    if (closetimer) {
        window.clearTimeout(closetimer);
        closetimer = null;
    }
}

// close layer when click-out
document.onclick = mclose;

$(window).load(function() {
    var maxHeight = 0;
    var lis = $("#sddm > li.mainMenuItem");

    // get max li height
    lis.each(function() { maxHeight = Math.max(maxHeight, this.offsetHeight); });

    // set max li height to all li elements - to not break the design
    lis.each(function() { $(this).height(maxHeight); });
});

$(document).ready(function() {
    // set the height of the menu div to match the height of the menues
    if ($("#sddm").length) {
        $("#menu").height($("#sddm").offset().height);
    }
});

$(document).resize(function() {
    // set the height of the menu div to match the height of the menues
    if ($("#sddm").length) {
        $("#menu").height($("#sddm").offset().height);
    }
});


Here is the CSS for the Menu:

#sddm
{
    margin: 0
;
    
padding: 0;
    
z-index: 100;
}
#sddm li
{
    margin: 0
;
    
padding: 0;
    
list-style: none;
    
float: left;
    
font: bold 11px arial;
}
#sddm li a
{
    display: block
;
    
margin: 0 1px 0 0;
    
padding: 4px 10px;
    
width: 60px;
    
color: #393;
    
text-align: center;
    
text-decoration: none;
}
#sddm li a:hover
{
    background: #3A4B58
;
}
#sddm div
{
    position: absolute
;
    
display: none;
    
margin: 0;
    
padding: 0;
    
background: #EAEBD8;
    
border: 1px solid #5970B2;
}
#sddm div a
{
    position: relative
;
    
display: block;
    
margin: 0;
    
padding: 5px 10px;
    
width: auto;
    
white-space: nowrap;
    
text-align: left;
    
text-decoration: none;
    
background: #EAEBD8;
    
color: #2875DE;
    
font: 11px arial;
}
#sddm div a:hover
{
    background: #4A4B48
;
    
color: #FFF;
}
#IE6Fix
{
    position: absolute
;
    
display: none;
    
margin: 0;
    
padding: 0;
    
border: 0;
}




Here is a demo HTML which demonstrates the use of the menu:


<div id="menu" style="clear: both;">
    <iframe id="IE6Fix" style="display: none;"></iframe>
    <ul id="sddm">
        <li class="mainMenuItem"><a href="#" onmouseover="mopen(this, 'categ1')" onmouseout="mclosetime()">
            Category Name</a>
            <div id='categ1' onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
                <a href="test.html">Subcategory 1</a> <a href="test.html">Subcategory 2</a> <a href="test.html">
                    Subcategory 3</a> <a href="test.html">Subcategory 4</a>
            </div>
        </li>
        <li class="mainMenuItem"><a href="#" onmouseover="mopen(this, 'categ2')" onmouseout="mclosetime()">
            Category Name 2</a>
            <div id='categ2' onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
                <a href="test.html">Subcategory 1</a> <a href="test.html">Subcategory 2</a> <a href="test.html">
                    Subcategory 3</a> <a href="test.html">Subcategory 4</a>
            </div>
        </li>
        <li class="mainMenuItem"><a href="#" onmouseover="mopen(this, 'categ3')" onmouseout="mclosetime()">
            Category Name 3</a>
            <div id='categ3' onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
                <a href="test.html">Subcategory 1</a> <a href="test.html">Subcategory 2</a> <a href="test.html">
                    Subcategory 3</a> <a href="test.html">Subcategory 4</a>
            </div>
        </li>
        <li class="mainMenuItem"><a href="#">Category Name 4</a> </li>
        <li class="mainMenuItem"><a href="#">Category Name 5</a> </li>
        <li class="mainMenuItem"><a href="#">Category Name 6</a> </li>
        <li class="mainMenuItem"><a href="#" onmouseover="mopen(this, 'categ7')" onmouseout="mclosetime()">
            Category Name 7</a>
            <div id='categ7' onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
                <a href="test.html">Subcategory 1</a> <a href="test.html">Subcategory 2</a> <a href="test.html">
                    Subcategory 3</a> <a href="test.html">Subcategory 4</a>
            </div>
        </li>
    </ul>
    <div class="clear">
    </div>
</div>

 Bowser Support






Code explained:

The menu itself is nothing else than a "ul" element. Each "li" element contains a link element - "a" . This "a" element will be like a trigger for the menu, when you hover the mouse over it, then it will display the "div" element below it, using the command: onmouseover="mopen(this, 'divElementIdToDisplay')".
To close the menu mclosetime() function is used, which will close the menu after some time (depends on the global variable - timeout).
This menu will also work on IE because behind the menu a iframe is displayed - manually, but this could also be achieved in an easier way - by using bgiframe jQuery plugin together with jQuery position.

Ending:


The menu will also rearrange when window is resized. You can create Sub-menu items and they will also work, see my HTML example as it includes such menus.

I did not write too much about this but if you have any suggestions please write and I will update the post.
I will accept also your suggestions for improvement.

1 comment:

Shea said...

Appreciate your bllog post

Post a Comment

your thoughts are welcome:

Need more? Leave comments and subscribe to my blog.

.