Fluid navigation items of different widths with equidistant spacing - css

I'd like to create a fluid horizontal navigation list of links, where the nav links are evenly spaced and take up the full width of the enclosing container . Nav links are all different widths. The first link should be left-aligned and the last link should be right aligned.
I've had limited success using techniques such as:
ul {display: table;width: 100%;}
li {display: table-cell;}
a {display: block;}
and also using
ul {text-align: justify}
li {inline-block }
but no code I've written seems to deal at all well with elements that are different widths. The spacing does not seem to stay equal as the nav is resized.
I need the nav to be fluid, first and last elements to be flush against the edge of the containing ul and for the elements to be equidistant from each other

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">Home</li>
<li>Collections</li>
<li class="tight">About Us</li>
<li>Slocklists</li>
<li class="tight">Trade Enquiries</li>
<li>Press</li>
<li class="last">Contact Us</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

You can use text-align: justify; and ignore the last left-justified row. #last is invisible and takes up the last row because of padding-left: 100%;: http://jsfiddle.net/mwRbn/
if you want to align the text of the menu vertically, use height in combination with line-height:
ul#nav {
text-align: justify;
width: 100%;
background: yellow;
height: 2em;
line-height: 2em;
}
http://jsfiddle.net/mwRbn/1/. Do you need a IE<8 hack?

Related

border-box not working on inline-block elements?

I have a list of inline-block elements, and I want to add a border to the element you hover over. However, notice how the border offsets the element, even when I use box-sizing: border-box and explicitly define the widths and heights of the elements. I illustrated the behavior below:
* { box-sizing: border-box }
ul { font-size: 0 }
li {
display: inline-block;
width: 100px; height: 40px; margin: 10px;
font-size: 20px; text-align: center;
background-color: #FFF176;
}
li:hover { border: 5px dashed grey }
<ul>
<li>hover</li>
<li>over</li>
<li>me!</li>
</ul>
The best solution I found is to use outline and outline-offset instead of border, but I'd really like to know why my original method doesn't work :/
UPDATE: While BoltClock gave a really great explanation and suggestion (which was all I was asking for), I just wanted to mention that I totally forgot about flexbox, which solved pretty much all the problems I was having with inline elements. I combined it with BoltClock's transparent border trick for my final JSFiddle solution
I see the problem now. What's happening is that box-sizing: border-box causes the content box of each element to shrink both horizontally and vertically once you add a border. Because your elements are inline-blocks, the vertical shrinking affects the baseline of the element being hovered, and therefore the baseline of the line it's on, resulting in the other elements on the same line being offset. If you look closely, you'll notice that the text actually stays aligned, which is the goal of offsetting the elements.
Changing the border to an outline works because outlines are designed to have no effect on layout (and also because you then take borders completely out of the picture).
However, it is for this reason that using an outline this way produces a significantly different effect from your original effect with a border. Setting an initial transparent border instead of an outline will ensure that your content stays offset the right amount whether the border is visible against the background (this was shown in a previous answer but it was deleted presumably because it was downvoted):
* { box-sizing: border-box }
ul { font-size: 0 }
li {
display: inline-block;
width: 100px; height: 40px; margin: 10px;
font-size: 20px; text-align: center;
background-color: #FFF176;
border: 5px dashed transparent;
}
li:hover { border-color: grey }
<ul>
<li>hover</li>
<li>over</li>
<li>me!</li>
</ul>

How to make menu items span across menu bar evenly

I have a site that I am working on.. and would like to know how to span the menu items evenly across the menu bar.
I know how to do it technically, but I want to make sure it's correct.
I want to be able to add another menu item and still have it look normal or overflow properly, etc.
The site is http://phillysuburbanhomes.com
Okay, so I added
.wpsight-menu li {
width:14%;
text-align:center
}
(more menu items were added)
My issue now is, that the full menu item doesn't show now
http://phillysubrubanhome.com - you can see what it's doing live
Currently there are 5 menu items, im assuming you would be adding another menu item thus making it 6 menu items.
The width of the container is 980px. Divide it in 6 parts which comes to 163.33. We need to give the width in percentage i.e 16.66%.
Set the width for each li as 16.66% and center-align the text.
Your css would be as follows:
ul#main-menu li{
width:16.66%;
text-align:center
}
Okay so I combined one of your methods with this this and it worked:
.wpsight-menu li {
width:14%;
text-align:center
}
.wpsight-menu a {
display: inline-block;
margin: 0 5px 0 0;
text-decoration: none;
}
Use CSS to give your anchors (a) the same padding and margins on the left and right and a percentage width of the li, like this;
ul#main-menu {
width: 100%;
}
ul#main-menu li {
width: 20%; /* for 5 menu items */
padding: 0;
margin: 0;
display: inline-block;
}
ul#main-menu li a {
padding: 0px 5px;
margin: 0px auto;
display: block;
}
and change the width of the li depending on the number of menu items.
That should do the trick!

inline-block goes under floated div

I'm trying to style my headings with display:inline-block; but i've a problem with a behavior of the property.
Jsfiddle : http://jsfiddle.net/Tu2GU/
See the titles, when a title has a long text, the heading goes under the floated div. I want the heading to break and then display 2 lines (or more) and stay on the left of the floated div, not under it.
Can't find anything helping, thanks !
edit : i updated the jsfiddle : http://jsfiddle.net/Tu2GU/13/ (removed % width for the floated div)
I don't want to have 2 divs side by side, the floated div on the right is meant to be right there, like a page summary giving link inside the page.
Also, heading are under the floated div (in html code) not over.
Since the right list uses a percent width, you can set a max-width with a percent width
h2 {
... Your original CSS ...
max-width:calc(75% - 40px); /* 40px comes from horizontal padding */
}
Demo
I'd recommend using a class to apply to each header instead of using the same max-width on each h1, h2, etc, but that's just personal preference
If the width of the right floated div is set, use calc(100% - 440px) or whatever the left horizontal padding + right width is
Create a float: left; container using the CSS below:
.lfloat {
float: left;
width: 75%;
}
You just have to wrap your text in a new div:
<div class="lfloat">
<!-- content -->
</div>
Demo
This will contain the content to the left and keep your sidebar to the right.
Note: You must clear your floats with clear: both;.
Why not try floating the elements on the left instead of using inline-block?
* {
font-family: Tahoma, Geneva, sans-serif;
font-weight: normal;
font-size: 1em;
}
.rfloat {
float: right;
width: 25%;
background: #9C3;
color: #111;
}
h1 {
float: left;
margin: 0;
padding: 10px 5%;
background: #06C;
color: #FFF;
}
h2 {
float: left;
margin: 0;
padding: 10px 5%;
background: #F33;
color: #FFF;
width:65%;
}
http://jsfiddle.net/g4Grv/
updated your Jsfiddle: http://jsfiddle.net/Tu2GU/12/
Main thing was a wrapping div around your h1 and p tag alongside of display:inline-block and vertical-align:top
Is this what you needed?
You have a few options. You can float the heading to the left so that it will slide up - you will have to set a width, though.
h2 {
float: left;
width: 80%;
}
Another option would be to set a max width. Since inline-block elements are technically block level, you can supply a width without breaking anything. You could try this:
h2 {
max-width: 80%
}
Yet another option would be to make the element inline. This will let the browser determine the best fit for the header.
h2 {
display: inline;
}
This will make the header wrap around the list and you may get the results you want. This method will make height and width parameters not work, so you will have to substitute those for line-height and padding

Automatic cell width on table-layout fixed, but the last cell

I want to create a breadcrumb for a website.
The elements should expand to fill all of the available space. If they couldn't fit on it, I'd like to have their inner text clipped with text-overflow: ellipsis, except the last one.
In other words: have the last element with the full width, and distribute the other ones on the remaining space (with width depending on their content, or if not possible at least they should't look bad...).
I tried with this code.
<div>
<ul>
<li>paka</li>
<li>ultrapaka</li>
<li>ultrapaka</li>
<li>ultrapaka ultrapaka</li>
<li>daslidjsajdsa</li>
</ul>
</div>
Here the CSS:
div {
display: table;
margin: 5px;
border:1px solid #777;
padding: 3px;
table-layout:fixed;
}
ul {
display: table-row;
}
li {
display: table-cell;
padding: 5px;
border: 1px solid #000;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
If I put table-layout: fixed the table really constrains its space, but I lose the automatic cell width.
If I don't put it, the table just overflows outside of its limits.
You can find a JsFiddle here. (I have set the table width to 400px to show the desired effect, even though on the final solution it should expand at 100%).
The best way to do that is using flexbox properties, but it's hard to make it work for all browsers.
You could maybe give a fixed width or percentage for all cells, and use the :last-child selector to apply an auto width on the last cell.
You could also try box-sizing, but I'm not sure about the result.

Centering Block Elements

First, I'll start off by saying that I do not have control over the HTML and this is what the HTML looks like:
<ul class="orbit-bullets">
<li>1</li>
<li>2</li>
</ul>
Second note I'll put is that I wouldn't mind using inline-block, but I need a fallback for non-supportive browsers.
I am trying to center the list. The list items must display inline but they must be block elements because I'm declaring a width, height and text-indent. So I know that I can use display:block; float:left;, but I would like the elements to be centered. And sometimes there are three list items or four or more, so I do not want to set a fixed width on the unordered list smaller than 500px. If I set the width to 500px, when there are just two or three list items, they appear very off centered.
I need to center the list items themselves within the unordered list while still using display:block; float:left;. Or is there a way to have a variable width on the unordered list while still having it centered? I'm up to any suggestions.
Thanks.
Try this one - http://jsfiddle.net/jD6yp/
ul {
width: 500px;
text-align: center;
background: beige;
}
li {
display: inline-block;
zoom: 1; /* for old IE-s */
*display: inline; /* for old IE-s */
padding: 10px;
margin: 20px;
border: 1px solid pink;
}

Resources