display:inline-block/text-align:justify下列表的两端对齐布局

这篇文章发布于 2011年03月13日,星期日,23:30,归类于 CSS相关。 阅读 294431 次, 今日 25 次 76 条评论

 

一、何为列表元素的两端对齐布局

首先说说何为两端对齐。下面这个截图是word中一段英文左对齐的右边缘截图:
word中文字左对齐截图 张鑫旭-鑫空间-鑫生活

可以看到右侧是参差不起的,现在,选中文字,点击word上方的两端对齐按钮,如下图:
点击word中两端对齐按钮 张鑫旭-鑫空间-鑫生活

结果文字的右边缘就成了这样:
word中两端对齐效果截图 张鑫旭-鑫空间-鑫生活

右侧完全对齐了,也就是整篇文字全部沿着左边缘和右边缘对齐显示了。

然后这里的“列表元素”指的是具有类似结构的重复列表元素,例如QQ校友中的图片列表:
列表布局示意  张鑫旭-鑫空间-鑫生活

所谓列表元素的两端对齐就是每行列表元素的第一个元素与父容器的左边缘重合,最后一个元素与父容器的右边缘重合。例如淘宝首页的热卖单品,或是人人网的热门分享列表(如下截图):
人人网热门分享列表两端对齐 张鑫旭-鑫空间-鑫生活

我想我们平时写页面的时候也经常会拿到的列表元素两端对齐的体验舒服的设计图吧。

二、如何实现元素的两端对齐

CSS2中text-align有一个属性值为justify,为对齐之意。其实现的效果就是可以让一行文字两端对齐显示(文字内容要超过一行)。如果您现在浏览器的地址是以http://www.zhangxinxu.com/打头的话,就可以发现我的每篇文章都是以两端对齐的方式显示的,所以,有时候就会出现文字非常稀松的情况,如下图所示。
文字两端对齐下稀疏的排列 张鑫旭-鑫空间-鑫生活

text-align其诞生的意义是控制文字的对齐与显示的,从其属性名上就可以看出来。从其渲染与解析上来看,其主要是用来控制inline水平元素或inline-block元素的对齐与显示的,例如嵌套行内标签的文字、图片、input表单控件等;而对block水平的元素是没有作用的。

所以,对于列表元素,理论上,我们只要将原本block水平的列表元素inline化或是inline-block化就可以轻松实现其两端对齐了。然而考虑到实际情况,inline水平化显然是不可能的,因为不能给列表元素定宽定高,设置垂直方向上的间距等,列表元素就像是一摊烂泥,根本没法用来砌房子;而inline-block化也是有重重阻碍的,因为IE6/7并不真正意义上的支持inline-block属性。

可见,理论上虽简单,实际上还得从长计议。

三、目前列表元素如何实现两端对齐的

方法多多。

① 首先看淘宝网首页热卖单品的例子,如下截图:
淘宝网首页热卖单品截图 张鑫旭-鑫空间-鑫生活

其列表布局使用的是传统的浮动(float)布局,通过width属性强行增大父容器的宽度来实现看上去的“两端对齐”效果的。
淘宝网首页增加容器宽度实现两端对齐 张鑫旭-鑫空间-鑫生活

② 再看人人网热门分享的两端对齐效果的实现方法,我在之前“基于display:inline-block的列表布局”一文中已经提过,人人网这里的列表布局为inline-block布局。
人人网热门分享inline-block布局 张鑫旭-鑫空间-鑫生活

其通过也是通过增大父标签的宽度来实现看上去的“两端对齐”效果的,不过其不是通过width属性来增加父标签的宽度的,而是使用的margin负值(我个人推荐使用margin负值而不是定宽):
margin负值增加父容器的宽度 张鑫旭-鑫空间-鑫生活

③ 还有一种方法就是利用white-space: nowrap,此方法需在inline-block布局基础上使用,一般用在实现单行列表元素看上去的“两端对齐”效果上。white-space: nowrap会强制列表元素不换行,于是你无需设定父标签容器的宽度或是通过margin负值等手段增加父容器的宽度等。这里不展开,以后有机会要好好说一说white-space: nowrap这个很有用的CSS声明的。

以上就是目前几种常见的实现列表元素看上去的“两端对齐”效果方法。怎么样,是不是每个都很折腾——首先列表元素排列就已经很折腾人的了(定宽,计算间距),然后还有人为增加父容器的宽度,同时祖辈元素还要溢出隐藏(overflow:hidden),oh,my lady嘎嘎。我想这就是为什么网上会有前端工程师咆哮体咆哮加班的原因了(关于此图,您可以点击这里查看)。

其实,这些活儿都是边喝咖啡,边看微博就可以完成的,关键使用text-align:justify来做两端对齐的布局。

四、text-align:justify实现两端对齐的好处

好处就是简单方便。只要一个简单的text-align:justify声明,里面的元素就自动等间距两端对齐布局啦!根本无需计算每个列表元素间的margin间距,更不用去修改父容器的宽度。

五、关于display:inline-block列表布局

本文的内容是基于display:inline-block列表布局的,所以如果您只知道浮动布局建议您认真看下我之前“拜拜了,浮动布局 – 基于display:inline-block的列表布局”一文,这篇文章非常详细的讲解了display:inline-block列表布局的前世今生,相信会有不少收获。您可能注意到了,在这篇文章的最后其实已经简单提到text-align:justify下的两端对齐布局,然而,那里只是简单展示了个例子,对于一些显示上的问题(例如悲剧的最后一行),以及IE6/7浏览器以及IE8浏览器下实现的注意事项并没有详细阐述,而本文就是来解决这些问题的。

六、两端对齐布局实践

为了表述上逻辑清晰。我们先把IE6和IE7浏览器晾在一边,看看IE8+浏览器以及现代浏览器下如何display:inline-block+text-align:justify实现列表元素的两端对齐。

说穿了其实很简单,我们不妨以最常见的列表标签-ul, li标签举例,要实现li列表的两端对齐,直接下面这点CSS代码就OK了:

ul{text-align:justify;}
li{display:inline-block;}

简单得让人当场吐血三升。

唯一需要注意的就是列表元素首尾标签留空(或换行),如下图所示:
标签首尾换行 张鑫旭-鑫空间-鑫生活首尾标签留空 张鑫旭-鑫空间-鑫生活

不能够上一个标签组的结束标签与下一个标签组的其实标签连在一起,如下图所示:
标签不能连在一起 张鑫旭-鑫空间-鑫生活

不仅如此,对于IE8浏览器,列表元素不能处在font-size:0的环境下,至少code>font-size:1px,因为IE8浏览器font-size:0或直接把换行空格或普通空格抹掉而无法实现两端对齐效果。

ok,下面是重头戏了,纠缠不清的IE6/IE7浏览器。显然上面的ul, li样式组合在IE6/7浏览器下是行不通的,即使你使用hack让IE6/7下的li标签有类似于display:inline-block的特性也是没有作用的。那么如何才能让IE6/7浏览器也有列表元素支持text-align:justify属性呢?经过我反复试验与调试,总结了两点:inline标签化以及结束标签连续化

1. inline标签化
所谓“inline标签化”就是列表元素需要使用inline水平的标签,例如span, a, strong, em等,像li, div这些标签就不可以。

2. 结束标签连续化
所谓“结束标签连续化”是指列表元素及其内部标签的结束标签需要连在一起。例如下面这个就是不行的:

<span>
    <a href="#">
        <img src="test.jpg" />
    </a>
    <span>描述</span>
</span>

而应该是这个样子滴:

<span>
    <a href="#">
        <img src="test.jpg" />
    </a>
    <span>描述</span></span>

我们已经习惯了结构化的缩进,所以上面结束标签连写看上去很不自然,有些别扭。但是,为了实现效果,这是没有办法的事情。注意:如果列表标签内嵌多层,则所有层级的结束标签都要连续。

IE6/IE7浏览器同时满足上面的inline标签化以及结束标签连续化,再加上先前现代浏览器下的首尾标签留空,IE6/IE7浏览器也就能够实现列表元素的两端对齐啦!

为了便于更直观的知道各个浏览器下实现两端对齐效果需要注意的事项,我特地制作了下表:

各个浏览器实现text-align:justify下的两端对齐布局注意事项表
浏览器 注意事项
IE6 inline水平列表标签、列表结束标签连续、列表元素间换行或留空
IE7 inline水平列表标签、列表结束标签连续、列表元素间换行或留空
IE8 列表元素间换行或留空、列表元素的环境字体大小不能为0
现代浏览器 列表元素间换行或留空

 

然而,现在还有一个很悲剧的问题没有解决,就是当列表元素最后一行无法两端对齐的悲剧。如下图所示:

text-justify下悲剧的最后一行

其实这个问题很好解决的。

如何悲剧变喜剧?
列表(或文字)要两端对齐的前提就是内容必须超过一行,所以,要解决最后一行元素无法两端对齐的文字其实很简单,就是在列表(或文字段)的最后创建一个高度为0的宽度100%的透明的inline-block的标签层就可以了,例如:

.justify_fix{display:inline-block; width:100%; height:0; overflow:hidden;}

如下HTML:

<span class="justify_fix"></span>

例如拿先前最后一行列表悲剧的demo举例,现在在该demo列表最后添加上面类名为justify_fixspan元素,结果最后一行两端对齐排列了,如下图变化:

末行元素也两端对齐了

无论您手上的是什么版本的浏览器,您可以狠狠地点击这里:末行也两端对齐的美女列表demo

改变浏览器的宽度,您可以更直观地看出两端对齐的效果。

补充 on 2011-03-16:
很多时候,我们希望列表的最后一行是左对齐排列的,而不是两端对齐,这时候怎么办呢?原理与上面的两端对齐一致。就是复制几个列表元素的外层标签,等宽,但高度为0,里面就是个&nbsp;(不可缺),复制的个数一般就是每行元素的列表个数啦,这样肯定可以保证最后一行元素一定是左对齐排列的啦!

如下HTML代码:

<div class="box">
    <span class="list"><img src="mm9.jpg" />
哇哦,美女,口水,鼻血~~~</span> <span class="list"><img src="mm9.jpg" />
哇哦,美女,口水,鼻血,不行了,我的小兔乱撞~~</span> . . . <span class="list left_fix">&nbsp;</span> <span class="list left_fix">&nbsp;</span> <span class="list left_fix">&nbsp;</span> <span class="list left_fix">&nbsp;</span> <span class="list left_fix">&nbsp;</span> </div>

上面HTML中的left_fix样式如下:

.left_fix{height:0; padding:0; overflow:hidden;}

结果先前等宽对齐的最后三个图片就与上面元素垂直对齐且左对齐啦!

最后一行元素左对齐实现

您可以狠狠地点击这里:最后一行元素左对齐排列demo

于是,综合上面所有讨论,我们就可以相对比较完美地实现列表元素在text-align:justify的两端对齐效果了。

补充于 2015-12-24
今天查看评论,发现好多小伙伴提到占位的元素会导致莫名的空白高度。关于这个问题,可参考“CSS深入理解vertical-align和line-height的基友关系”的“④ 复杂现象”部分。有分析原因以及提供解决方案。

七、text-align:justify下两端对齐效果实例

我们拿上面人人网热门分享处的inline-block列表布局举例。

您可以狠狠地点击这里:人人网热门分享列表text-justify下两端对齐demo

效果如下图(截自IE7浏览器):
人人网热门列表justify下两端对齐效果截图 张鑫旭-鑫空间-鑫生活

CSS代码如下:

.video-list{width:540px; margin-left:auto; margin-right:auto; text-align:justify;} /*列表父容器*/
.text-justify-list{display:inline-block; width:97px; margin-bottom:15px; text-align:left; vertical-align:top;}/*列表元素*/

.
. /* 完全人人网CSS代码*/
.

.justify_fix{display:inline-block; width:100%; height:0; overflow:hidden;}/*末行悲剧变喜剧*/

可以看到列表元素压根就没有设置垂直方向上的margin或是padding值,就单单一个宽度值,但是列表元素确实两端对齐,等间距排列。没有计算,没有有意去增加父容器宽度等,超简单就实现了。

比对上面提到的些注意事项,看看这个例子中的HTML代码是如何实践上面的注意事项的:
① inline水平标签
如下截图所示:
inline标签使用截图 张鑫旭-鑫空间-鑫生活

② 列表结束标签连续
如下图所示:
结束标签连续 张鑫旭-鑫空间-鑫生活

③ 列表标签换行或留空
如下截图所示:
列表标签换行不连续截图 张鑫旭-鑫空间-鑫生活

如此,你也可以轻轻松松实现列表元素的两端对齐布局,而且不用去担心兼容性问题!!GO!大胆的去使用吧!

八、一些补充的话语

首先是关于为什么IE6/IE7浏览器列表元素需使用inline水平标签,,同时结束标签需连续,这个问题我也是不知道为什么,浏览器不是我开发的,要问得去问盖茨先生了。

其次,语义化的问题。要兼顾IE6/IE7浏览器,像列表标签ul, li等就不能使用了,所以HTML语义上可能质量要降低了,权衡在你手。

最后,都是自己试验出来的东西,可能您有更好的方法,或者可以解释IE6/IE7下一些奇怪的行为,欢迎以评论的形式进行交流。资历有限,文中难免有表述不准确的地方,欢迎指正。

最后的最后为日本罕见大地震祈福!

(本篇完)

分享到:


发表评论(目前76 条评论)

  1. 前端的油菜花说道:

    我用span 和 li 都试了下,span比较简单实现,代码量比较少,缺点是缺少语义化。li的话兼容IE67比较鸡肋,display:inline-block display:inline要同时设置,不能断开的样子,然后文字和图片都水平化了(同一条线上显示),文字达不到图片下居中的效果,可能还有其他方法,咳咳,能力有限,暂时想不出来。不过,对于图片和文字的,一般文字都是起说明作用,比较推荐dl dt dd语义的标签,然后,晚点去试一下哈哈0 0!

  2. TMQ说道:

    在用两端对齐时,列表元素间一定要留空或者换行,这样在自己能控制html代码的地方还好。如果列表元素是用模板引擎repeat出来的就比较难搞了。一方面动不了模板引擎代码,另一方面justify又需要有空格的限制,头疼。

  3. bornkiller说道:

    如果在响应式布局下,单行数目不固定,实现最后一行左对齐好像比较尴尬。这种情况应该如何处理?

  4. levis说道:

    人人网的这个示例不太明白,为什么span标签连续,列表项之间又不连续?

  5. 一个水货说道:

    其实在span标签后面跟上一个/$nbsp; 就可以解决换行还是不换行了

  6. Capricair说道:

    最近有用两端对齐做列表,但是后端用js填充元素,两端对齐会失效,变成一坨,不知道博主是否遇到过这种情况,有何解决办法?

  7. M27说道:

    关于为什么IE6/IE7浏览器列表元素需使用inline水平标签? 这是因为在IE6/7下由block–>inline-block(*display:inline;*zoom:1;)的元素之间是不存在空格间隙的,所以两端对齐对这些元素无效,而由inline–>inline-block的元素之间是存在空格间隙的(IE6/7下),所以justify属性值对这些元素生效. 而IE8+和现代浏览器不管是inline–>inline-blick还是block–>inline-block的元素,它们之间是存在空格间隙的,所以justify属性对其生效.

    text-align:justify;属性在大多数的浏览器下生效的前提是,需要在汉字间插入有空白,如空格。

  8. storm说道:

    这种方式并不可取
    主要原因是布局的显示依赖编写方式,比如换行,空格什么的,然而这些列表都是后端输出的,而且输出后渲染到页面的过程中很大情况会做压缩空格处理,所以,连在一块的情况太常见了.

  9. 老七说道:

    鑫哥你好,这篇文章老早前看过一遍,今天又看了一遍,做实验的过程中,发现 最后添加的 height 为0、用来充当最后一行的元素,会导致整个 box 高度增加,查了下资料,有人说是 HTML5 的 DOCTYPE 会导致这种情况,鑫哥是否了解这个情况?我们如何解决这个问题?

    • 张 鑫旭说道:

      @老七 貌似使用vertical-align: bottom控制可以解决此问题~

      • 老七说道:

        多谢回复,试了一下还是不行,确定是 DOCTYPE 的问题,换成老式的 DOCTYPE 立马没有此问题了,我的权宜之计是确定外部容器的 height …. 具体问题原因暂时没搞懂 …

        • 任光辉说道:

          这个问题困扰好多遍了,用 font-size: 0; 倒是可以解决,不过相对应的在 ie8,9 下呵呵了。
          另外用 text-align: justify; 去做布局是不是有些背离的此属性本身的意义?(原本不该只是去做排版的么)

          感觉最后还是要用 flex 干这种事情的好。

        • 老七说道:

          我说的这个问题 font-size:0 也没办法解决,用 justify 简单实现单行对称 还是很方便的,刚发现个解决办法,就是在设置了 text-align:justify 的元素上 设置 line-height:0, 然后伪行(:after)要设置 vertical-align:top, 这个办法唯一的缺点是”子元素要恢复 line-height 值”,但是省得算 height 的精确值了,旭哥,你看这方法可取吗?

          参考:http://stackoverflow.com/a/21031803/4741763

        • 任光辉说道:

          只可惜多行的情况下,这个方法没用

  10. 其文说道:

    那个,inline-block元素之间的那个空白有什么办法避免么?
    就是下面那个小周说的“用列表布局每个宽度20%,但是一行只排了4个就换行了。跟之间不换行才可以排5个。纠结o(>﹏<)o…”

  11. 小周说道:

    用列表布局每个宽度20%,但是一行只排了4个就换行了。跟之间不换行才可以排5个。纠结o(>﹏<)o…

  12. bob说道:

    博主知道订餐小秘书么?

  13. riophae说道:

    太厉害了哈哈哈学习了 赞~

  14. Bob说道:

    最近发现了一个IE专用的属性text-align-last让最后一行也两端对齐;让其余的浏览器使用after伪类生成最后一行。这样就省去了后面的那个fix。
    从这里看到的:http://demo.doyoe.com/css3/justify/justify-for-all.htm

  15. 木枫说道:

    另外我想最后一行要左对齐,然后要占位元素,怎么算出每行最大的列数,想了好久,求解答。。。

  16. 木枫说道:

    关于这点:列表元素间换行或留空,因为用的是js模板,会被压缩,实现不了,有什么解决方法么?

  17. hopana说道:

    如果列表元素不是span,而是一个个的div,用这种方法最后一行只能两端对齐,用你这种方法实现不了左对齐,不知道为什么,求解答。。。。。。。

  18. 袁源说道:

    本来想捐一块钱表示感谢,结果发现不支持信用卡付款

  19. lestorm说道:

  20. huly说道:

    你好,有个问题想请教你?有两个span,2222 都用了display:inline-block; 为什么两个span不能在一行对齐。但span里都加上文字就可以,这是为什么啊?求解,谢谢

  21. Svin说道:

    页面渲染后再添加元素,新添加的元素不能实现对齐,请问有什么好的解决办法

  22. candoudou说道:

    最后一行不是两端对齐很坑爹,所以这种方法还是不能用!

  23. 周Ali说道:

    这个需要重新再测测,很有问题哦!

  24. 荔枝说道:

    我很好奇你怎么会想到要尝试用 连续来解决这样的问题的?

  25. Ivan说道:

    你好,对于你所说的“结束标签连续化”还有一种方法来实现。就是加“&nbsp” 在ie6、7 如果连着的inline标签会连在一起,即去除之间的Whitespace。以下是我简易html,不知道能否发送?

    1111

     

    2222

     

  26. 54yuri说道:

    > 第二个问题,如你所说,可以在列表结尾再加一个元素,然后使用justify_fix属性,这个方法我试过了,在除IE6,7浏览器下,会使包容元素增加一个明显的高度,解决这个问题的方法,我暂时只找到了给包容元素定高,可是这样做的话就使其实用性大打折扣,也许你可以找到更好的解决办法.

    my answer: 那个是由于外层的div的line-height引起的,因为最后一个span独占了一行把外层的div的line-height给挤到下面去了

    我的方法是:

    .outter_div {
    line-height: 0px;
    font-size: 0px;
    letter-spacing: 0px;
    -webkit-text-size-adjust: none;
    }

    让外面的div应用这个class即可

  27. 54yuri说道:

    to 夏雨
    >第一个问题,不管是list还是span,如果其相加的width之和小于外面包
    容元素(如ul)的width,则justify不会起作用,这个缺点在实际应用中会造成很多问题。

    my answer: 有下面radom的方法设置text-justify+
    lz的 http://www.zhangxinxu.com/wordpress/?p=2357

    > 第二个问题,如你所说,可以在列表结尾再加一个元素,然后使用justify_fix属性,这个方法我试过了,在除IE6,7浏览器下,会使包容元素增加一个明显的高度,解决这个问题的方法,我暂时只找到了给包容元素定高,可是这样做的话就使其实用性大打折扣,也许你可以找到更好的解决办法.

    my answer: 那个是由于外层的div的line-height引起的,因为最后一个span独占了一行把外层的div的line-height给挤到下面去了,所以给最外层的”line-height:0px;” 就行
    这里是

    错误的话欢迎指出

  28. vimest说道:

    附带一提,这对里面item的限制应该要作要求,如七个span, 3 3 1,最后一个看起来没问题,但当八个的时候,3 3 2, 最后一个span则后向右对齐,这也属正常行为,但不是我们想要的

  29. 一丝冰凉说道:

    须知 IE下面 有text-justify 这个私有属性实现两端对齐,然后为什么最后一行不能两端对齐?其实这个不是bug,w3c规范有相关说明。此文的思路是非常好的,但是有些东西容易误导读者。

  30. 爱爱爱说道:

    最后一行左对齐是个悲剧

  31. diaoyude说道:

    bug太多!

  32. ryan说道:

    《text-justify实现更具语义化的图文列表布局》
    http://www.ryanbay.com/?p=220

    请博主多多指教!

  33. River说道:

    如果是纯文字的,ie6下面会有问题

  34. 直觉说道:

    张总 可以参照下优酷的视频列表页,我一般写图片列表基本上就是按照那种方法来写的,可以保证列表相对容器左右两侧对齐

  35. OWL说道:

    写得很好,学习了。不过,综合各种心得,为了样式而放弃语义,我不赞成,这是美工自私的想法,同样,能用列表就尽量用列表,也是对程序员的尊重。一家之言

  36. 马云说道:

    这位兄台句句在理,但我还是有要说的话,我坚持用列表。

  37. 堂主说道:

    文章很好,很用心!但确实是上面几位朋友提出的意见:为了强调某种样式效果而无视元素本身的语义,从而牺牲页面结构的语义化是不可取的。就像之前的那个浮动,本身主要是为实现文字围绕图片这个效果来的,结果被广泛用于布局;而目前这个针对内联元素中文本两端对齐的属性也被用来作为布局。

  38. gelin说道:

    同意 一楼的 看法
    这样写 列表 就没了语义
    在css失效的情况下 页面会变得一塌糊涂

  39. wewe说道:

    确实在FF2下面 很悲剧

  40. xiaokai说道:

    给我整个例子看看我正了半天没整出来,谢谢

  41. xiaokai说道:

    你这种方法很好,能不能用这种方法作出图片(宽度高度不固定)相对于父级DIV(宽度高度固定)水平垂直居中

  42. misstian2008说道:

    当最后一行大于一或小于能排列到个数时,.justify_fix{display:inline-block; width:100%; height:0; overflow:hidden;}就不起作用了。

  43. xp说道:

    想法是好的。但实际操作时没有人会去用。人家的列表都是程序循环出来。哪来的 inline水平标签

  44. 答案说道:

    还是那句话.这是我见过的最好的最用心的博客.

  45. 小不点说道:

    没事就喜欢来你家瞅瞅 呵呵

  46. radom说道:

    中文段落两端对齐(单行不动)需两者才能兼容哦!
    text-align:Justify(火狐);text-justify:inter-ideograph(IE)

  47. 夏雨说道:

    鑫旭你好,这篇文章的技术在实际应用中存在2个不可忽视的问题。

    第一个问题,不管是list还是span,如果其相加的width之和小于外面包容元素(如ul)的width,则justify不会起作用,这个缺点在实际应用中会造成很多问题。

    第二个问题,如你所说,可以在列表结尾再加一个元素,然后使用justify_fix属性,这个方法我试过了,在除IE6,7浏览器下,会使包容元素增加一个明显的高度,解决这个问题的方法,我暂时只找到了给包容元素定高,可是这样做的话就使其实用性大打折扣,也许你可以找到更好的解决办法.

  48. jenney说道:

    FireFox 2.0下有问题!

  49. gsid说道:

    每篇文章都很精彩

  50. 濯焰说道:

    这样实现的话,语义就没了