如何在HTML+CSS中真正地证明水平菜单的必要性?

88

在HTML中,你可以找到大量关于菜单栏的教程,但对于这个特定的(尽管我认为是通用的)情况,我没有找到任何合适的解决方案:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • 文本菜单项数量不定,页面布局自适应。
  • 第一个菜单项应左对齐,最后一个菜单项应右对齐。
  • 其余菜单项应在菜单栏上最佳分布。
  • 由于数量不定,因此无法预先计算最佳宽度。

请注意,在这里,表格也不起作用:

  • 如果您将所有TD居中,第一个和最后一个项目将无法正确对齐。
  • 如果您左对齐和右对齐第一个和最后一个项目,则间距将不理想。

很奇怪,使用HTML和CSS没有明显的方法以清晰的方式实现这一点,不是吗?

14个回答

84

最简单的方法是通过在行末插入一个元素来强制换行,这个元素将占用剩余空间以上的空间,然后将其隐藏。我用一个简单的span元素很容易地实现了这个效果:

#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>

#menu span选择器内的所有垃圾(据我所知)都是为了迎合大多数浏览器而必需的。它应该强制span元素的宽度为100%,这应该会导致换行,因为由于display: inline-block规则的缘故,它被认为是一个内联元素。inline-block还使得span可以接受块级样式规则(如width),这会导致元素无法与菜单在同一行上,并因此导致菜单换行。

当然,您需要根据您的用例和设计调整span的宽度,但我希望您能理解这个基本思路并加以适应。


1
如果使用span而不是hr,似乎可以工作!但实际上并没有起作用,HR占据了可见空间 - 使用#menu { border: solid 1px green; }进行确认。此外,对于不是自然内联元素的元素,在IE(...7?CompatibilityView?)上display: inline-block;无法正常工作。HR是块级元素,因此我猜测在IE上inline-block对HR不起作用。总之,使用span。 - ANeves
9
功能良好,但如果您将每个空格替换为 (n个空格),则结果会更漂亮,这样菜单&项目&1就会紧密地保持在一起。在Safari浏览器中 无效。 - yitwail
15
我刚刚花了一个多小时的时间,一直苦思冥想为什么这个问题对我不起作用。答案是我需要在标签之间添加空格。我正在使用WordPress,wp_page_menu在每个<li></li>后面没有包含换行符。通过使用preg_replace添加它们,现在它可以正常工作了。成功解决问题,呼~ - Greg Perham
1
现在要让它正常工作,您必须在支持它的浏览器中对LIs使用display: inline-block,并在不支持它的浏览器中使用display: inline,将空格替换为  。另外,由于ie7不支持inline-block,您必须在force-wrap标记上使用inline,并将足够的 塞入其中以强制换行。 - Joren
1
为了澄清Joren的评论,UL仍然需要是inline,而LI需要是inline-block。 - Moss
显示剩余11条评论

44

现代方法 - Flexboxes

现在CSS3弹性盒子浏览器支持更好了,我们中的一些人终于可以开始使用它们了。只需添加其他供应商前缀以获得更多浏览器覆盖率

在这种情况下,您只需将父元素的display设置为flex,然后将justify-content属性更改为space-betweenspace-around,以在子弹性盒子项之间或周围添加空间。

使用 justify-content: space-between - (这里是示例)

ul {
    list-style: none;
    padding: 0;
    margin: 0;
}
.menu {
    display: flex;
    justify-content: space-between;
}
<ul class="menu">
    <li>Item One</li>
    <li>Item Two</li>
    <li>Item Three Longer</li>
    <li>Item Four</li>
</ul>


使用 justify-content: space-around - (此处示例)

ul {
    list-style: none;
    padding: 0;
    margin: 0;
}
.menu {
    display: flex;
    justify-content: space-around;
}
<ul class="menu">
    <li>Item One</li>
    <li>Item Two</li>
    <li>Item Three Longer</li>
    <li>Item Four</li>
</ul>


“justify-content: space-around”解决方案如果菜单不能适应,则不会插入任何换行符。 - Caleb Stanford

12

好的,这个解决方案在IE6/7上不起作用,因为缺乏对:before/:after的支持,但是:

ul {
  text-align: justify;
  list-style: none;
  list-style-image: none;
  margin: 0;
  padding: 0;
}
ul:after {
  content: "";
  margin-left: 100%;
}
li {
  display: inline;
}
a {
  display: inline-block;
}
<div id="menu">
  <ul>
    <li><a href="#">Menu item 1</a></li>
    <li><a href="#">Menu item 2</a></li>
    <li><a href="#">Menu item 3</a></li>
    <li><a href="#">Menu item 4</a></li>
    <li><a href="#">Menu item 5</a></li>
  </ul>
</div>

我将a标签设置为inline-block的原因是我不想使标签内的文字也变得对齐,而我也不想使用不间断空格。


谢谢!你的解决方案非常直接明了。如果我是提问者,我会将其标记为答案。 - Andrevinsky
我在IE浏览器上使用时遇到了问题。在谷歌中花费了几个小时后,我终于找到了这篇博客文章:https://kristinlbradley.wordpress.com/2011/09/15/cross-browser-css-justified-block-list/。最终解决的方法是在“ul”选择器中添加`text-justify: distribute-all-lines;`。似乎是IE的专有内容。 - maryisdead
不确定是否有影响,但我使用了 width: 100% 而不是 margin-left: 100%ul:after{content:""; margin-left:100%;} - Eugene Song

8

有一个解决方案。在Firefox、IE6、IE7、Webkit等浏览器中都有效。

请确保在关闭标签前不要加任何空格。否则,IE6会出现问题。

您可以选择给.outer设置宽度。

.outer {
  text-align: justify;
}
.outer span.finish {
  display: inline-block;
  width: 100%;
}
.outer span.inner {
  display: inline-block;
  white-space: nowrap;
}
<div class="outer">
  <span class="inner">THE MENU ITEMS</span>
  <span class="inner">SHOULD BE</span>
  <span class="inner">JUSTIFIED</span>
  <span class="inner">JUST AS</span>
  <span class="inner">PLAIN TEXT</span>
  <span class="inner">WOULD BE</span>
  <span class="finish"></span>
</div>


对我来说,这个解决方案在Gecko浏览器上运行良好,但在WebKit浏览器(使用Chromium、Midori、Epiphany测试)上不起作用:在WebKit中,最后一项后面有尾随空格。 - flight
确保每个span之间只有一个空格字符,最后一个内部span和结束标签之间有两个空格字符。如果这样不行,请调整一下空格字符。Webkit中存在一个bug。 - mikelikespie
1
.outer {text-align: justify} .outer span.finish {display: inline-block; width: 100%} .outer span.inner {display: inline-block; white-space: nowrap} 在IE6和IE7上无法正常工作。 - Binyamin
.outer设置line-height:0会强制该列表高度呈现为一个单行。 - kontur

3
适用于 Opera、Firefox、Chrome 和 IE。
ul {
   display: table;
   margin: 1em auto 0;
   padding: 0;
   text-align: center;
   width: 90%;
}

li {
   display: table-cell;
   border: 1px solid black;
   padding: 0 5px;
}

1
很遗憾,由于缺少table-cell支持,这在IE 7中无法正常工作。 - Philipp Michael

3

另一种解决方案。我没有添加区分类等处理html的选项,所以我找到了一个纯css的方法。

在Chrome、Firefox和Safari中有效,不确定IE是否有效。

测试:http://jsfiddle.net/c2crP/1

ul {
  margin: 0; 
  padding: 0; 
  list-style: none; 
  width: 200px; 
  text-align: justify; 
  list-style-type: none;
}
ul > li {
  display: inline; 
  text-align: justify; 
}

/* declaration below will add a whitespace after every li. This is for one line codes where no whitespace (of breaks) are present and the browser wouldn't know where to make a break. */
ul > li:after {
  content: ' '; 
  display: inline;
}

/* notice the 'inline-block'! Otherwise won't work for webkit which puts after pseudo el inside of it's parent instead of after thus shifting also the parent on next line! */
ul > li:last-child:after {
  display: inline-block;
  margin-left: 100%; 
  content: ' ';
}
<ul>
  <li><a href="#">home</a></li>
  <li><a href="#">exposities</a></li>
  <li><a href="#">werk</a></li>
  <li><a href="#">statement</a></li>
  <li><a href="#">contact</a></li>
</ul>


2
将其设置为具有text-align: justify<p>吗?
更新:算了。那根本不像我想的那样。
更新2:现在除了IE之外的任何浏览器都不起作用,但CSS3支持这种形式的text-align-last

太快了!我以为我的解决方案很独特,但你在短短几分钟内就提出了类似的东西。 - flight
现在在2016年,text-align-last在除Safari以外的任何浏览器中都可以使用(http://caniuse.com/#feat=css-text-align-last)。这应该是[flexbox方法](https://dev59.com/1HVD5IYBdhLWcg3wNY1Z#29188477)或[span hack](https://dev59.com/1HVD5IYBdhLWcg3wNY1Z#2444135)的可能对手。 - hakatashi

1
对于基于Gecko的浏览器,我想出了这个解决方案。但是这个方案在WebKit浏览器中不起作用(例如Chromium、Midori、Epiphany),它们仍然会在最后一个项目之后显示尾随空格。
我将菜单栏放在了一个“两端对齐的段落”中。问题是两端对齐的段落的最后一行不会被呈现为两端对齐,这是显而易见的原因。因此,我添加了一个宽的不可见元素(例如img),它保证段落至少有两行长。
现在,菜单栏由浏览器用于两端对齐纯文本的相同算法来对齐。
代码:
<div style="width:500px; background:#eee;">
 <p style="text-align:justify">
  <a href="#">THE&nbsp;MENU&nbsp;ITEMS</a>
  <a href="#">SHOULD&nbsp;BE</a>
  <a href="#">JUSTIFIED</a>
  <a href="#">JUST&nbsp;AS</a>
  <a href="#">PLAIN&nbsp;TEXT</a>
  <a href="#">WOULD&nbsp;BE</a>
  <img src="/Content/Img/stackoverflow-logo-250.png" width="400" height="0"/>
 </p>
 <p>There's an varying number of text-only menu items and the page layout is fluid.</p>
 <p>The first menu item should be left-aligned, the last menu item should be right-aligned. The remaining items should be spread optimal on the menu bar.</p>
 <p>The number is varying,so there's no chance to pre-calculate the optimal widths.</p>
 <p>Note that a TABLE won't work here as well:</p>
 <ul>
  <li>If you center all TDs, the first and the last item aren't aligned correctly.</li>
  <li>If you left-align and right-align the first resp. the last items, the spacing will be sub-optimal.</li>
 </ul>
</div>

备注:你有注意到我作弊了吗?为了添加空格填充元素,我必须猜测菜单栏的宽度。因此,这个解决方案并不完全符合规则。


请注意,如果您在列表项上设置display:inline,则也可以在此处使用列表... 列表对于菜单来说更为常规。 - Andrew Vit
@Andrew Vit:你说得对。这基本上也是asbjornu的解决方案所建议的。 - flight
@flight,如果您认为我的答案是解决方案,请标记它为解决方案好吗?现在看起来您的问题还没有得到解决。如果已经解决了,那么提供您找到的解决方案或将任何提供的答案标记为解决方案会很好。 :-) - Asbjørn Ulsberg

1

只有当句子自然地导致换行时,文本才会被调整对齐。因此,您所需要做的就是自然地强制换行,并隐藏第二行的内容:

CSS:

ul {
  text-align: justify;
  width: 400px;
  margin: 0;
  padding: 0;
  height: 1.2em;
  /* forces the height of the ul to one line */
  overflow: hidden;
  /* enforces the single line height */
  list-style-type: none;
  background-color: yellow;
}

ul li {
  display: inline;
}

ul li.break {
  margin-left: 100%;
  /* use e.g. 1000px if your ul has no width */
}

HTML:

<ul>
  <li><a href="/">The</a></li>
  <li><a href="/">quick</a></li>
  <li><a href="/">brown</a></li>
  <li><a href="/">fox</a></li>
  <li class="break">&nbsp;</li>
</ul>

li.break元素必须与最后一个菜单项在同一行,并且必须包含一些内容(在这种情况下是非断空格),否则在某些浏览器中,如果它不在同一行,则会在行末看到一些小的额外空间,如果它不包含任何内容,则会被忽略并且该行不会对齐。

在IE7、IE8、IE9、Chrome和Firefox 4中进行了测试。


0

如果选择使用JavaScript,这是可能的(此脚本基于mootools)

<script type="text/javascript">//<![CDATA[
    window.addEvent('load', function(){
        var mncontainer = $('main-menu');
        var mncw = mncontainer.getSize().size.x;
        var mnul = mncontainer.getFirst();//UL
        var mnuw = mnul.getSize().size.x;
        var wdif = mncw - mnuw;
        var list = mnul.getChildren(); //get all list items
        //get the remained width (which can be positive or negative)
        //and devided by number of list item and also take out the precision
        var liwd = Math.floor(wdif/list.length);
        var selw, mwd=mncw, tliw=0;
        list.each(function(el){
            var elw = el.getSize().size.x;
            if(elw < mwd){ mwd = elw; selw = el;}
            el.setStyle('width', elw+liwd);
            tliw += el.getSize().size.x;
        });
        var rwidth = mncw-tliw;//get the remain width and set it to item which has smallest width
        if(rwidth>0){
            elw = selw.getSize().size.x;
            selw.setStyle('width', elw+rwidth);
        }
    });
    //]]>
</script>

和 CSS

<style type="text/css">
    #main-menu{
        padding-top:41px;
        width:100%;
        overflow:hidden;
        position:relative;
    }
    ul.menu_tab{
        padding-top:1px;
        height:38px;
        clear:left;
        float:left;
        list-style:none;
        margin:0;
        padding:0;
        position:relative;
        left:50%;
        text-align:center;
    }
    ul.menu_tab li{
        display:block;
        float:left;
        list-style:none;
        margin:0;
        padding:0;
        position:relative;
        right:50%;
    }
    ul.menu_tab li.item7{
        margin-right:0;
    }
    ul.menu_tab li a, ul.menu_tab li a:visited{
        display:block;
        color:#006A71;
        font-weight:700;
        text-decoration:none;
        padding:0 0 0 10px;
    }
    ul.menu_tab li a span{
        display:block;
        padding:12px 10px 8px 0;
    }
    ul.menu_tab li.active a, ul.menu_tab li a:hover{
        background:url("../images/bg-menutab.gif") repeat-x left top;
        color:#999999;
    }
    ul.menu_tab li.active a span,ul.menu_tab li.active a.visited span, ul.menu_tab li a:hover span{
        background:url("../images/bg-menutab.gif") repeat-x right top;
        color:#999999;
    }
</style>

并且最后一个 HTML

<div id="main-menu">
    <ul class="menu_tab">
        <li class="item1"><a href="#"><span>Home</span></a></li>
        <li class="item2"><a href="#"><span>The Project</span></a></li>
        <li class="item3"><a href="#"><span>About Grants</span></a></li>
        <li class="item4"><a href="#"><span>Partners</span></a></li>
        <li class="item5"><a href="#"><span>Resources</span></a></li>
        <li class="item6"><a href="#"><span>News</span></a></li>
        <li class="item7"><a href="#"><span>Contact</span></a></li>
    </ul>
</div>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接