88

You find plenty of tutorials on menu bars in HTML, but for this specific (though IMHO generic) case, I haven't found any decent solution:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • There's an varying number of text-only menu items and the page layout is fluid.
  • The first menu item should be left-aligned, the last menu item should be right-aligned.
  • The remaining items should be spread optimally on the menu bar.
  • The number is varying,so there's no chance to pre-calculate the optimal widths.

Note that a TABLE won't work here as well:

  • If you center all TDs, the first and the last item aren’t aligned correctly.
  • If you left-align and right-align the first resp. the last items, the spacing will be sub-optimal.

Isn’t it strange that there is no obvious way to implement this in a clean way by using HTML and CSS?

Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
flight
  • 1,738
  • 2
  • 14
  • 14

14 Answers14

84

The simplest thing to do is to is to force the line to break by inserting an element at the end of the line that will occupy more than the left available space and then hiding it. I've accomplished this quite easily with a simple span element like so:

#menu {
  text-align: justify;
}

#menu * {
  display: inline;
}

#menu li {
  display: inline-block;
}

#menu span {
  display: inline-block;
  position: relative;
  width: 100%;
  height: 0;
}
<div id="menu">
  <ul>
    <li><a href="#">Menu item 1</a></li>
    <li><a href="#">Menu item 3</a></li>
    <li><a href="#">Menu item 2</a></li>
  </ul>
  <span></span>
</div>

All the junk inside the #menu span selector is (as far as I've found) required to please most browsers. It should force the width of the span element to 100%, which should cause a line break since it is considered an inline element due to the display: inline-block rule. inline-block also makes the span possible to block-level style rules like width which causes the element to not fit in line with the menu and thus the menu to line-break.

You of course need to adjust the width of the span to your use case and design, but I hope you get the general idea and can adapt it.

Nhan
  • 3,595
  • 6
  • 30
  • 38
Asbjørn Ulsberg
  • 8,721
  • 3
  • 45
  • 61
  • 1
    **It seems to work if you use a `span` instead of an `hr`!** It's not really working, the HR is occupying visible space - use `#menu { border: solid 1px green; }` to confirm. Also, `display: inline-block;` does not work on IE (...7? CompatibilityView?) for elements that aren't naturally inline elements. HR is a block element, so I'm guessing inline-block doesn't work for HR on IE. Anyway, span. – ANeves Apr 07 '10 at 11:30
  • Also, make it `width: 100%;` instead of `width: 900px;`. Edit that and switch the hr for a span and you'll get an upvote - and most likely marked as answer. ;) – ANeves Apr 07 '10 at 11:36
  • I haven't tested my changes, but I think my solution should reflect your thoughts now. – Asbjørn Ulsberg Apr 13 '10 at 11:55
  • @sr pt correct IE7 and lower. IE7 compatibility should not work either. – corymathews Apr 14 '10 at 19:03
  • 9
    works great, but results are prettier if you replace each space with   (n space), so that Menu & item & 1 stay close together.   doesn't work in safari. – yitwail Aug 09 '11 at 19:39
  • @yitwail, which spaces are you thinking about? With your reputation, I guess you should be able to edit my answer and make your suggested improvements? – Asbjørn Ulsberg Aug 29 '11 at 23:06
  • Note that you don't need to have an extra `` element, you can also use the `:after` pseudoclass and the `content` property to have the same effect. Probably won't work in IE < 9 though. – Husky Oct 16 '11 at 16:27
  • Or instead replacing spaces with ` `'s you can set `text-align:left;` on items. – Litek May 18 '12 at 10:39
  • 15
    I just spent over an hour beating my head against the wall, trying to figure out why this wasn't working for me. The answer is that I needed white space between the tags. I am working in WordPress, and wp_page_menu does not include line breaks after each
  • . Added them using preg_replace, and it works now. Fwewww – Greg Perham May 30 '12 at 05:43
  • 1
    To get this to work these days you have to use display: inline-block on the LIs in browsers that support it, and use display: inline with spaces replaced by   on browsers that don't support it. Also since ie7 doesn't support inline-block, you have to use inline on your force-wrap tag and just shove enough  s in it to force the wrap. – Joren Aug 08 '12 at 20:22
  • @Joren, thanks for your suggestions! Please feel free to update the answer with your suggestions, or send the updated code example to me so I can do it. – Asbjørn Ulsberg Aug 09 '12 at 13:27
  • 1
    To clarify Joren's comment, the UL still needs to be inline and the LI's inline-block. – Moss Sep 04 '13 at 02:55
  • This works great! However, I'm failing to add top/bottom paddings to my li's. The padding is hidden as overflow. Any way to add visible padding to the items? – Manuela Hutter Jun 17 '14 at 13:12
  • Looks great! What is the purpose of the `position: relative;` style on the span tag though? – elju Apr 05 '15 at 00:58
  • Important note: there must be blank characters (`\t` or `\n` or `space`) between li elements. This means inside the template .. `?>
  • ` and ` – Stphane Dec 11 '15 at 16:14
  • Hmm, this is nice, but inserts extra vertical space after the menu. Not sure how to remove it. – Caleb Stanford Jan 10 '23 at 03:06