19 Eylül 2012 Çarşamba

Even up a horizontal menu with javascript

Often I receive a design which calls for some arbitrary number of menu items to be spread across a horizontal navigation menu not evenly, but each with the same amount of padding between the text and the divider on each side.
Simple text representation:
| text | longer text | mid-text | t |

Padding is easy, it goes on the link. Nominally you then just adjust it as necessary once you know what the final text items are. (I usually assign any extra space to li:first-child a );

Unfortunately different browsers will
- display the same font slightly wider
- calculate/render elements with slightly different widths

Which means your perfectly calculated style will either fall short or push the last menu item over the edge.

My solution is to initially set the padding fairly small, and then expand it on page load with a little javascript. This is progressive - the site is still perfectly usable if you don't have javascript turned on, it may just look a little lopsided.

Basic navigation structure

<div id="navigation">
<ul id="menu">
<li>< href="">text</li>
<li>< href="">longer text</li>
<li>< href="">med-text</li>
<li>< href="">t</li>
</ul>
</div>


Basic styling
#navigation { width: 980px; }
#navigation * { margin: 0; padding: 0}
#navigation li {float: left; list-style-type: none;}
#navigation a { padding: 0 10px; }
/* if you do end up with a spare px through rendering this next rule makes sure the RHS
of your menu sits pretty.
.ie-last is added to older versions of IE using feature detection*/
#navigation li:last-child, #navigation li.ie-last { float: right; }


The javscript (jQuery)
$(document).ready(function(){
//distribute menu items to fit page width

cont_width = $('#navigation').innerWidth(); //container width
var nav_width = 0; //space taken up by items as rendered
menu_items = $('#menu > li');
$(menu_items).each(function(){
nav_width += $(this).outerWidth();
});
space = cont_width - nav_width;

if(space>0){
//relies on default padding being set up correctly in the stylesheet

link_padding = parseInt($(menu_items).first().children('a').first('a').css('paddingRight'));
num_items = menu_items.length
widen_each = space/num_items; //float

while(widen_each>=1){
//expand all items

each_side = widen_each/2;
each_side = Math.floor(each_side);
if(each_side>=1){ // ie widen_each >= 2, add to both sides equally
link_padding += each_side;
widen_each -= each_side*2;
space -= each_side*2*num_items;
$(menu_items).children('a').css({'paddingLeft':link_padding,'paddingRight':link_padding});
} else { // ie 1 <= widen_each < 2, add 1 to the right
widen_each -= 1;
space -= num_items;
$(menu_items).children('a').css({'paddingRight':link_padding+1});
}
}

if(widen_each>0){ // some amount of space cannot be evenly distributed as whole px
//expand last item to take up any remaining space
last_item = $(menu_items).last();
while(space>1){ //in the browsers that are causing trouble we need to leave a px free.
each_side = space/2;
each_side = Math.floor(each_side);

if(each_side>=1){
link_padding += each_side;
space -= each_side*2;
$(last_item).children('a').css({'paddingLeft':link_padding,'paddingRight':link_padding});
} else {
space -= 1;
$(last_item).children('a').css({'paddingRight':link_padding+1});
}
}
}
}
);


~~~
A pure CSS approach is to use display:table-cell (see this article at SitePoint).

Hiç yorum yok:

Yorum Gönder