Fluid Navigation Items of Different Widths with Equidistant Spacing

Fluid navigation items of different widths with equidistant spacing

I thought about this for a while and came up with two reasonable approaches, both of which are pretty good but not exactly pixel perfect. One is CSS based only and the second is aided by jQuery (JavaScript).

CSS Approach - pretty good approximation

Consider the following HTML:

<ul class="nav ex1">
<li class="first"><a href="#">Home</a></li>
<li><a href="#">Collections</a></li>
<li class="tight"><a href="#">About Us</a></li>
<li><a href="#">Slocklists</a></li>
<li class="tight"><a href="#">Trade Enquiries</a></li>
<li><a href="#">Press</a></li>
<li class="last"><a href="#">Contact Us</a></li>
</ul>

I added some classes as hooks for the styling.

The CSS is as follows:

.nav.ex1 {
outline: 1px dashed blue;
width: 100%;
margin: 0;
padding: 0;
display: table;
}
.nav.ex1 li {
display: table-cell;
outline: 1px dotted gray;
width: 20%;
white-space: pre;
text-align: center;
}
.nav.ex1 li.first {
width: 1%;
}
.nav.ex1 li.last {
width: 1%;
}
.nav.ex1 li.tight {
width: 1%;
}

In Example 1, the ul.nav parent container uses display: table and width: 100%. The child li elements are table-cell's. I added white-space: pre to prevent some of the links from wrapping into two lines, and text-align: center to keep the text centered.

The trick is to force some of the table-cell's to shrink-to-fit the text, and you can do this by setting width: 1% which is non-zero but too small to hold the text (unless your screen is 10,000 pixels wide). I shrink-to-fit the first and last cells which forces them to align to the left and right edges of the parent container. I then force every other table-cell to shrink-to-fit by added the .tight class.

The remaining table's cells will have a width of 20% which will keep them evenly spaced between their two nearest neighbors. HOWEVER, there will be some slight variation in spacing among the links in the row, which is why I call it an approximation.

jQuery Aided Solution

In Example 2, the markup is essentially the same and the CSS is:

.nav.ex2 {
outline: 1px dashed blue;;
margin: 0;
padding: 0;
display: block;
overflow: auto;
width: 100%;
}
.nav.ex2 li {
float: left;
display: block;
outline: 1px dotted gray;
width: auto;
}

In this case, the li elements are floated left and I use width: auto.

The trick is to calculate the magic left-margin value and apply it to all the li elements except for the first one.

The jQuery action is:

$(window).resize(function () {
navResizer();
});

// On load, initially, make sure to set the size.
navResizer();

function navResizer() {
var $li_w = 0;
var $ul_w = $(".nav.ex2").innerWidth();

$( ".nav.ex2 li" ).each(function( index ) {
$li_w += $(this).innerWidth();
});

var li_margin = Math.floor(($ul_w-$li_w)/6);
$(".nav.ex2 li").not(".first").css("margin-left",li_margin);
$("p.note").text( "Widths: ul.nav: " + $ul_w + " all li: " + $li_w + " Left margin: " + li_margin);
}

Basically, the action calculates the width of ul.nav ($ul_w), and the total widths of all the li child elements ($li_w).

The left-margin is calculated by ($ul_w - $li_w)/6 where 6 is the number of gaps between the 7 links.

The key line of code is: $(".nav.ex2 li").not(".first").css("margin-left",li_margin);

I use .not(".first") to omit the first li element and then .css to set the left margin.

The one slight defect is at the far right where the link is not quite right justified, but you can fix that by floating the last li to the right.

For the most part, if your link texts were similar in length, you would be hard pressed to distinguish the two. Both approaches are not quite pixel perfect, but pretty good.

Fiddle: http://jsfiddle.net/audetwebdesign/xhSfs/

Footnote
I tried some other approaches using text-align: justify and inline-block, but the CSS engine does not treat inline-blocks like regular words, so will not justify a line of inline-blocks.

Setting left-margin to a % value will not quite work at some window widths and the right-most link will not be on the edge as desired.

The jQuery approach has been tried before, see:

Evenly-spaced navigation links that take up entire width of ul in CSS3

Fluid navigation items of different widths with equidistant spacing

I thought about this for a while and came up with two reasonable approaches, both of which are pretty good but not exactly pixel perfect. One is CSS based only and the second is aided by jQuery (JavaScript).

CSS Approach - pretty good approximation

Consider the following HTML:

<ul class="nav ex1">
<li class="first"><a href="#">Home</a></li>
<li><a href="#">Collections</a></li>
<li class="tight"><a href="#">About Us</a></li>
<li><a href="#">Slocklists</a></li>
<li class="tight"><a href="#">Trade Enquiries</a></li>
<li><a href="#">Press</a></li>
<li class="last"><a href="#">Contact Us</a></li>
</ul>

I added some classes as hooks for the styling.

The CSS is as follows:

.nav.ex1 {
outline: 1px dashed blue;
width: 100%;
margin: 0;
padding: 0;
display: table;
}
.nav.ex1 li {
display: table-cell;
outline: 1px dotted gray;
width: 20%;
white-space: pre;
text-align: center;
}
.nav.ex1 li.first {
width: 1%;
}
.nav.ex1 li.last {
width: 1%;
}
.nav.ex1 li.tight {
width: 1%;
}

In Example 1, the ul.nav parent container uses display: table and width: 100%. The child li elements are table-cell's. I added white-space: pre to prevent some of the links from wrapping into two lines, and text-align: center to keep the text centered.

The trick is to force some of the table-cell's to shrink-to-fit the text, and you can do this by setting width: 1% which is non-zero but too small to hold the text (unless your screen is 10,000 pixels wide). I shrink-to-fit the first and last cells which forces them to align to the left and right edges of the parent container. I then force every other table-cell to shrink-to-fit by added the .tight class.

The remaining table's cells will have a width of 20% which will keep them evenly spaced between their two nearest neighbors. HOWEVER, there will be some slight variation in spacing among the links in the row, which is why I call it an approximation.

jQuery Aided Solution

In Example 2, the markup is essentially the same and the CSS is:

.nav.ex2 {
outline: 1px dashed blue;;
margin: 0;
padding: 0;
display: block;
overflow: auto;
width: 100%;
}
.nav.ex2 li {
float: left;
display: block;
outline: 1px dotted gray;
width: auto;
}

In this case, the li elements are floated left and I use width: auto.

The trick is to calculate the magic left-margin value and apply it to all the li elements except for the first one.

The jQuery action is:

$(window).resize(function () {
navResizer();
});

// On load, initially, make sure to set the size.
navResizer();

function navResizer() {
var $li_w = 0;
var $ul_w = $(".nav.ex2").innerWidth();

$( ".nav.ex2 li" ).each(function( index ) {
$li_w += $(this).innerWidth();
});

var li_margin = Math.floor(($ul_w-$li_w)/6);
$(".nav.ex2 li").not(".first").css("margin-left",li_margin);
$("p.note").text( "Widths: ul.nav: " + $ul_w + " all li: " + $li_w + " Left margin: " + li_margin);
}

Basically, the action calculates the width of ul.nav ($ul_w), and the total widths of all the li child elements ($li_w).

The left-margin is calculated by ($ul_w - $li_w)/6 where 6 is the number of gaps between the 7 links.

The key line of code is: $(".nav.ex2 li").not(".first").css("margin-left",li_margin);

I use .not(".first") to omit the first li element and then .css to set the left margin.

The one slight defect is at the far right where the link is not quite right justified, but you can fix that by floating the last li to the right.

For the most part, if your link texts were similar in length, you would be hard pressed to distinguish the two. Both approaches are not quite pixel perfect, but pretty good.

Fiddle: http://jsfiddle.net/audetwebdesign/xhSfs/

Footnote
I tried some other approaches using text-align: justify and inline-block, but the CSS engine does not treat inline-blocks like regular words, so will not justify a line of inline-blocks.

Setting left-margin to a % value will not quite work at some window widths and the right-most link will not be on the edge as desired.

The jQuery approach has been tried before, see:

Evenly-spaced navigation links that take up entire width of ul in CSS3

Fluid navigation items of different widths with equidistant spacing

I thought about this for a while and came up with two reasonable approaches, both of which are pretty good but not exactly pixel perfect. One is CSS based only and the second is aided by jQuery (JavaScript).

CSS Approach - pretty good approximation

Consider the following HTML:

<ul class="nav ex1">
<li class="first"><a href="#">Home</a></li>
<li><a href="#">Collections</a></li>
<li class="tight"><a href="#">About Us</a></li>
<li><a href="#">Slocklists</a></li>
<li class="tight"><a href="#">Trade Enquiries</a></li>
<li><a href="#">Press</a></li>
<li class="last"><a href="#">Contact Us</a></li>
</ul>

I added some classes as hooks for the styling.

The CSS is as follows:

.nav.ex1 {
outline: 1px dashed blue;
width: 100%;
margin: 0;
padding: 0;
display: table;
}
.nav.ex1 li {
display: table-cell;
outline: 1px dotted gray;
width: 20%;
white-space: pre;
text-align: center;
}
.nav.ex1 li.first {
width: 1%;
}
.nav.ex1 li.last {
width: 1%;
}
.nav.ex1 li.tight {
width: 1%;
}

In Example 1, the ul.nav parent container uses display: table and width: 100%. The child li elements are table-cell's. I added white-space: pre to prevent some of the links from wrapping into two lines, and text-align: center to keep the text centered.

The trick is to force some of the table-cell's to shrink-to-fit the text, and you can do this by setting width: 1% which is non-zero but too small to hold the text (unless your screen is 10,000 pixels wide). I shrink-to-fit the first and last cells which forces them to align to the left and right edges of the parent container. I then force every other table-cell to shrink-to-fit by added the .tight class.

The remaining table's cells will have a width of 20% which will keep them evenly spaced between their two nearest neighbors. HOWEVER, there will be some slight variation in spacing among the links in the row, which is why I call it an approximation.

jQuery Aided Solution

In Example 2, the markup is essentially the same and the CSS is:

.nav.ex2 {
outline: 1px dashed blue;;
margin: 0;
padding: 0;
display: block;
overflow: auto;
width: 100%;
}
.nav.ex2 li {
float: left;
display: block;
outline: 1px dotted gray;
width: auto;
}

In this case, the li elements are floated left and I use width: auto.

The trick is to calculate the magic left-margin value and apply it to all the li elements except for the first one.

The jQuery action is:

$(window).resize(function () {
navResizer();
});

// On load, initially, make sure to set the size.
navResizer();

function navResizer() {
var $li_w = 0;
var $ul_w = $(".nav.ex2").innerWidth();

$( ".nav.ex2 li" ).each(function( index ) {
$li_w += $(this).innerWidth();
});

var li_margin = Math.floor(($ul_w-$li_w)/6);
$(".nav.ex2 li").not(".first").css("margin-left",li_margin);
$("p.note").text( "Widths: ul.nav: " + $ul_w + " all li: " + $li_w + " Left margin: " + li_margin);
}

Basically, the action calculates the width of ul.nav ($ul_w), and the total widths of all the li child elements ($li_w).

The left-margin is calculated by ($ul_w - $li_w)/6 where 6 is the number of gaps between the 7 links.

The key line of code is: $(".nav.ex2 li").not(".first").css("margin-left",li_margin);

I use .not(".first") to omit the first li element and then .css to set the left margin.

The one slight defect is at the far right where the link is not quite right justified, but you can fix that by floating the last li to the right.

For the most part, if your link texts were similar in length, you would be hard pressed to distinguish the two. Both approaches are not quite pixel perfect, but pretty good.

Fiddle: http://jsfiddle.net/audetwebdesign/xhSfs/

Footnote
I tried some other approaches using text-align: justify and inline-block, but the CSS engine does not treat inline-blocks like regular words, so will not justify a line of inline-blocks.

Setting left-margin to a % value will not quite work at some window widths and the right-most link will not be on the edge as desired.

The jQuery approach has been tried before, see:

Evenly-spaced navigation links that take up entire width of ul in CSS3

Fluid layout with even width navigation items

This is the simplest way I could think of:

ul { overflow: hidden; padding-left: 100px; position: relative; }
li { width: 33.33%; float: left; }
li:first-child { position: absolute; top: 0; left: 0; width: 100px; }

The main idea is taking the first li out of the flow (position: absolute) and adding a padding-left to the ul (space for the first li). Now if we set the percentage width for the other lis, they will take up the remaining space.

And here is a jsFiddle Demo. I added a red border on the ul which shows that because of the percentages lis will not accurately fill it.

I am unsure what mobile browsers you want to support, but except :first-child (which can be worked around by adding a class on the first list item) I assume they must support everything I used.

Flexible horizontal navigation with equal distance between nav elements

You can remove the empty divs if you add box-pack: justify:

ul {
-webkit-box-pack: justify;
-moz-box-pack: justify;
box-pack: justify;
}

See: http://jsfiddle.net/thirtydot/wKjVq/3/

Preventing Div Elements From Wrapping in a Fluid Navigation

I found a solution that works in all browsers, specifically IE6 on up since that was a requirement for this project. If you have something better that accomplishes the same thing please post! My sincere thanks to everyone who answered/helped.

<div id="header2">
<table id="headerContent2">
<tr>
<td id="left" valign="top">
<div id="leftWrapper">
<div class="search-list2">Search List</div>
<div class="social-buttons2">Social Buttons</div>
</div>
</td>
<td id="middle"> </td>
<td id="right" valign="top">
<div class="signin2">Login Drop Down</div>
</td>
</tr>
</table>
</div>

<style>
#header2 {
background: #404040;
height: 35px;
color: white;
margin: 0 0 12px 0;
}

#headerContent2 {
width:100%;
}

#headerContent2 td {
height: 32px;
padding:0;
margin:0;
}

.search-list2 {
width:150px;
float:left;
background:#039;
}

.social-buttons2 {
width:200px;
float:left;
background:#060;
}

.signin2 {
background:#F00;
float:right;
width:400px;
}

#leftWrapper {
width:400px;
}

#middle {
width:100%;
}
</style>

See Demo of it working here. Copy the code and try it out in all the IE's since JSfiddle does not work in all IE's.
http://jsfiddle.net/GtXKE/

Full width horizontal nav bar with evenly spaced items

With a static number of elements it's easy - http://jsfiddle.net/X56cJ/

However, if you want to have uniform spacing between the text, not the elements themselves - it becomes tricky. Again, if the number of elements doesn't change, you do it with css classes and static widths. Otherwise it'll have to be javascript

EDIT: Here's the JavaScript way of getting same space between elements.

HTML:

<ul class="menu">
<li>About Us</li>
<li>Our Products</li>
<li>FAQs</li>
<li>Contact</li>
<li>Login</li>
</ul>

JS:

function alignMenuItems(){
var totEltWidth = 0;
var menuWidth = $('ul.menu')[0].offsetWidth;
var availableWidth = 0;
var space = 0;

var elts = $('.menu li');
elts.each(function(inx, elt) {
// reset paddding to 0 to get correct offsetwidth
$(elt).css('padding-left', '0px');
$(elt).css('padding-right', '0px');

totEltWidth += elt.offsetWidth;
});

availableWidth = menuWidth - totEltWidth;

space = availableWidth/(elts.length);

elts.each(function(inx, elt) {
$(elt).css('padding-left', (space/2) + 'px');
$(elt).css('padding-right', (space/2) + 'px');
});
}

Full example here

Fixed spacing between elements of different sizes in a responsive design

Check this solution, might be helpfull: "text-align: justify;" inline-block elements properly?

For old IE's there's text-align-last property.

Space NavBar elements such that centers are equidistant?

If you are sure that you will have only those four links, you can add this CSS code to your styles and it should work:

.links > * {
width: 20%;
text-align: center;
}

or

.links > img, .links > li {
width: 20%;
text-align: center;
}

This way you make sure that every item will take equal amount of space and its content will be centered.



Related Topics



Leave a reply



Submit