[转]拜拜了,浮动布局——基于inline-block的列表布局
本文其实真实题目应该是“CSS float浮动的深入研究、详解及拓展(三)”,但是为了更显著的体现本文的核心内容,顺便搞点噱头吸引眼球,同时跟百哥和谷妹玩点小暧昧,就改成了现在这个样子了。
一、一抹前言
没有爱的日子,时间如指尖细沙,不知不觉就流逝了。写“ CSS float浮动的深入研究、详解及拓展(一)”和“ CSS float浮动的深入研究、详解及拓展(二)”似乎就在不久前,然而相隔差不多有一年之久了。文章最后留下了“浮动布局更好的替代方案是什么?”后文再介绍的预告。
由于自己肚子中的货物不足以撑起一篇足够质量的文章,所以关于“浮动布局更好的替代方案是什么?”的文章一直并未动笔。好在事物总是在发展的,我也是每天都是在进步,对于列表布局的思考也愈发成熟。加上正好前不久又有人询问我“浮动布局的替换方案是什么”,于是觉得介绍“浮动布局的替换方案“的时机成熟了。
要注意,本文的布局专指列表布局。就是具有相同DOM结构的水平排列可以repeat出来的列表元素。如QQ校友中的图片列表(图片截自老同学相册,已隐去名字):
二、列表浮动布局的局限
列表浮动布局就是指通过使用float属性,让列表元素依次排列的布局(通常是左浮动,float:left)。这是相当常见的也是目前最最主流的列表布局方式,所以这里就不吃咸鱼蘸酱油——多此一举show代码了。
对于浮动局部的局限性,想必同行们都知道,就是每个列表元素的高度必须要一致,否则就会像是俄罗斯方块一样,“锯齿相错”,例如一个左浮动列表布局,如果第一行有个列表高度高于其他列表,那就在第二行,第一个元素会沿着最高元素的右侧对齐,此原因是属于恶魔系的float属性破坏了inline box(具体参见前两篇float的文章)。
那发展势头的新浪微博举例,其右侧人气用户的模块,姓名是限高的,现在用firebug去掉这个限高属性,看看结果会如何:
可以看到,列表元素就像是俄罗斯方块卡错了位置一样,错开了。所以,如果我们要使用浮动布局列表,就必须限高,于是,不得已,需要裁掉超多的用户名信息。
浮动本身就是个魔鬼,所以,使用浮动布局还需要修复其带来的副作用——高度塌陷的问题,也就是常提到的“清除浮动”了。
另外,IE6下,重复的列表元素会出现莫名的bug,例如出现不知哪来的文字。
由于浮动布局学习与理解的门槛低,又受主流之风驱动,且满足了绝大多数的布局需求。所以,在追寻更好的布局方式上,大家的愿望似乎不是很强烈,当然,可能是自己井底之蛙了,不过从国内领先网站的列表布局情况来看,貌似浮动布局还是扎根很深的(这回在列表布局上,人人网让我刮目了一把)。
三、提一提表格布局
在表格布局时代,我们基本上不用担心列表元素高度不一会错位的问题,表格中的单元格(td标签)自动回等高关联,且水平列表项还包裹在tr等标签中,所以,不可能发生错位。所以,很正常的,我们会想到利用类似table属性的样式进行一些布局,例如display:table; display:table-row; display:table-cell;等属性,对此我是建议您看看支付宝UED的“ 基于display:table的CSS布局”一文,如果不是受制于IE6/7对这些display属性不支持,基于display:table的CSS布局定会大放异彩的。
如果想使用display:table的CSS布局来代替浮动布局,权衡来看,不见得有提高。好的地方在于,不要担心列表元素的高度不一,甚至自动等高。撇开兼容性不谈,其还有不足在于在后台循环输出时需要水平repeat下,然后再垂直repeat。既然这样,还不如使用浮动布局,因为在这种情况下,使用浮动也是可以不用担心高度不一的问题的,这就是facebook好友列表的做法(oh, 该死,facebook上不去,想截个图的)。所以,基本上,使用display:table的CSS布局来代替浮动布局是完全占据下风的。因而,这里就不瞎子点灯白费蜡了,如一丝寒意的秋风,轻轻带过。
四、另外一种display属性,display:inline-block
display:inline-block感觉与display:table-cell有些相似,例如对内部元素的包裹性。但是,由于display:inline-block最大的不同就是其没有父元素的匿名包裹特性,这使得display:inline-block属性的使用非常自由,可与文字,图片混排,可内嵌block属性元素,可以可以置身于inline水平的元素中。可谓黑白通吃,左右逢源。
inline-block属性的元素适用于inline box模型,所以,当其中的列表元素高度不一时,是不会有错位的。关于line box模型,我在之前的“ css行高line-height的一些深入理解及应用”第二部分提到了,以及前面“ CSS float浮动的深入研究、详解及拓展(一)”一文的“浮动的破坏性”部分中做过比较详细的介绍。一言以蔽之,就是每一行所有的inline元素和inline-block元素会共同形成一个line boxes,这个line box的高度由里面最高的元素决定。所以,即使inline-block属性的列表元素高度异常,撑开的是整个line boxes的高度,因而,不会与下一行的列表元素发生错位。如下面的我自己画得拙劣的示意图所示的:
根据一些前辈的说法,IE6/7不支持display:inline-block属性,只是可以让标签有类似于inline-block的属性,起初我也是接受这种说法的,不过后来又表示了怀疑,最近使用text-align:justify;做测试的时候的一些样式表现证实了:确实IE6/7是不支持display:inline-block属性,只是让其表现的跟inline-block一样,尤其对于inline水平的元素,其表现度可以用perfect一词来形容了。
对于IE8+以及现代浏览器,直接使用:
display:inline-block;
就可以了,支持任意水平的元素。
对于不支持的IE6/7浏览器该怎么办呢?如果是inline水平的元素(如a标签,span标签之类)跟上面一样,直接:
display:inline-block;
就可以了,对于这两个浏览器,其功效与*zoom:1;是一样的。
如果是block水平的元素,例如li标签。则需要多点代码,目前我知道的方法有两个,如下所示:
li {display:inline-block;...}li {display:inline;}
或者是:
li{display:inline; zoom:1;...}
block水平的inline-block化的元素与inline水平的在表现层又是有差异的,这个后面会谈到。
五、一点小阻挠:inline-block元素间的换行符空格间隙问题
在完整的展示兼容性的像素级的inline-block元素列表布局前,有必要讲讲使用display:inline-block列表布局经常会遇到的“换行符/空格间隙问题”。
如果inline-block元素间有空格或是换行产生了间隙,那是正常的,应该的。如果没有空格与间隙才是不正常的(IE6/7 block水平元素)。真正的inline-block元素,就像个图片一样。例如,两个不在一行的img标签,形成的两个图片之间就会有间隙,如下图所示:
您可以狠狠地点击这里: 换行符与inline-block元素间间隙demo
要让这些空格不出现,最简单的最容易理解的就是让列表的结束标签与下一个列表的开始标签连在一起,就像是:
但是,这种做法好傻啊,而且HTML代码的可读性很不好。尤其考虑到现实情况:后台人员可能不清楚标签换行对样式的影响,直接后台repeat的时候,换行了。所以,此方法顶多临时应付些小打小闹的地方,要想广泛使用,显然业余了。
其实,我们只要细细想想,空格符本质上就是个字符,与a,b,c,d这些字符是个同一个属性的东西,只是他是空格,透明的看不见而已(但可以选中)。所以,只要我们使用让文字宽度为0的那些方法,不就可以解决inline-block元素间换行符间隙的问题啦!
于是,很自然而然的,想到了以下样式:
font-size:0;
我还记得我大学时候解决IE6下标签高度无法小于11像素bug时的方法,就是使用的font-size:0;这属性可以让莫名的空格啊神马东东的都变成浮云。我们来试下,可否用来消除inline-block元素间的换行间隙的问题。
如下测试代码:
现在来看看几个主流浏览器下的反应,首先是Firefox浏览器:
希望越大失望越大,Chrome下的空格对于font-size:0貌似很不屑一顾。唉,这个问题在Chrome还是婴儿阶段的时候就有了,到现在还没有改过来。
经过我稀里哗啦一轮番的浏览器测试,发现就只有Chrome浏览器对font-size:0置若罔闻,连同样内核的Safari都不会这样。
您可以狠狠地点击这里: font-size:0清除换行符间隙demo
虽然Chrome目前国内份额有限,但是毕竟有不少高端用户在用啊。所以,一旦Chrome不支持font-size:0,那么,此方法显然不能应用到实际项目中,在加上IE和Safari下还是有1像素的间隙,所以此方法就此刷掉。现在得去考虑其他更好的方法。
我们一起开动脑筋,还有什么CSS属性可以影响文字的水平间距的。啊,只见脑袋上灯泡一亮,有了,不是有个letter-spacing属性嘛。可以控制文字间的水平距离的,支持负值,可以让文字水平方向上重叠(line-height是让文字垂直方向上重叠)。哟呵呵,心动不如行动,还拿那两张美女图片做示例,看看letter-spacing属性可否抹掉换行符产生的间距。参见如下测试代码:
先来看看一向表现优异的Firefox3.6浏览器,如下图:
嘿嘿,也是不错,希望就在眼前,看看IE新生主力军IE8下的表现:
再来看看对inline-block属性不支持的IE7浏览器:
您可以狠狠地点击这里: letter-spacing去除间隙空格
结果让人心里咯噔了一下,晕那,好像没有作用诶!不会啊,我很清楚的记得即使IE6/7浏览器也是支持letter-spacing去空格的。后来我仔细对比,发现这里的letter-spacing:-4px是有所用的,只是默认的空格间隙比较大,所以,即使舍去4像素间距,还是有一段空白内容。但是,我清楚的记得在Arial字体下,空格间距应该是兼容的,为何这里就IE6/IE7浏览器下不一样呢?后来,我反复测试,终于发现了一个第一次看到的bug,“冒号影响了空格间隙的水平大小”。现将“效果:”这个小标题后面的中文冒号还成英文的,结果,空格间距一下子兼容了,真是很神奇,我不得不佩服IE6/7浏览器的怪异指数。
您可以狠狠地点击这里: IE6/7下冒号英文化后间隙demo
IE6/7浏览器本身就是个怪胎,喜欢不走寻常路。在这两个浏览器下,block水平的元素inline-block化之后,会直接忽略换行符的间隙。所以,这里还有一点点疑虑没有解决。既然IE6/7浏览器div,li 这些标签inline-block化后没有空格间隙,那么使用letter-spacing负值会不会让列表元素水平重叠呢?啊,这个问题就不要担心了,如果元素间本身没有空格,使用letter-spacing属性是不会发生水平重叠的问题的。例如下面的测试代码:
结果如下图:
也就是说,如果元素间本身就没有间隔,无论letter-spacing的值多小,元素都不会重叠。但是,如果有空格间隙存在,letter-spacing会发生重叠的。所以,我们无需为IE6/7元素会不会重叠这个问题担心了。
您可以狠狠地点击这里: letter-spacing与无间隙重叠demo
六、顺着letter-spacing属性走更远些
按照我自己浅薄的经验开看,换行符产生的空格与按一下space键的作用是一样的。此空格所撑开的水平距离受空格字符所在环境的字体以及字体影响。由于不同的网站使用的字体不一样,所以,在借助letter-spacing属性实现列表布局时的letter-spacing的取值可能就不一样,所以,我专门研究了下空格水平间距与字体/字体大小之间的关系,以及兼容性等,整理在下面的表格中了,希望能对您有所帮助,要是发现不准确之处欢迎指正:
letter-spacing与字体大小/字体关系的数据表
Firefox 3.6.12 | Chrome 7.0 | Safari 4.0(win) | Opera 10.51 | IE8 | IE6/7 | 是否兼容 | |
---|---|---|---|---|---|---|---|
16px/Arial | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
14px/Arial | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
13px/Arial | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
12px/Arial | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
16px/Tahoma | -5px | -5px | -5px | -4px留空1px/-5px弹开还原 | -5px | -5px | 不兼容(仅Opera) |
14px/Tahoma | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
13px/Tahoma | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
12px/Tahoma | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
16px/Verdana | -6px | -6px | -6px | -5px留空1px/-6px弹开还原 | -6px | -6px | 不兼容(仅Opera) |
14px/Verdana | -5px | -5px | -5px | -4px留空1px/-5px弹开还原 | -5px | -5px | 不兼容(仅Opera) |
13px/Verdana | -5px | -5px | -5px | -4px留空1px/-5px弹开还原 | -5px | -5px | 不兼容(仅Opera) |
12px/Verdana | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
16px/Geneva | -8px | -8px | -8px | -7px留空1px/-8px空隙还原 | -8px | -8px | 不兼容(仅Opera) |
14px/Geneva | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
13px/Geneva | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
12px/Geneva | -6px | -6px | -6px | -5px留空1px/-6px空隙还原 | -6px | -6px | 不兼容(仅Opera) |
16px/Georgia | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
14px/Georgia | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
13px/Georgia | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
12px/Georgia | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
16px/Times New Roman | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
14px/Times New Roman | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
13px/Times New Roman | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
12px/Times New Roman | -3px | -3px | -3px | -2px留空1px/-3px空隙还原 | -3px | -3px | 不兼容(仅Opera) |
16px/Courier New | -10px | -10px | -10px | -9px留空1px/-10px空隙还原 | -10px | -10px | 不兼容(仅Opera) |
14px/Courier New | -8px | -8px | -8px | -7px留空1px/-8px空隙还原 | -8px | -8px | 不兼容(仅Opera) |
13px/Courier New | -8px | -8px | -8px | -7px留空1px/-8px空隙还原 | -8px | -8px | 不兼容(仅Opera) |
12px/Courier New | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
16px/monospace | -8px | -8px | -8px | -7px留空1px/-8px空隙还原 | -8px | -8px | 不兼容(仅Opera) |
14px/monospace | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
13px/monospace | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
12px/monospace | -6px | -6px | -6px | -5px留空1px/-6px空隙还原 | -6px | -6px | 不兼容(仅Opera) |
16px/宋体 | -8px | -8px | -8px | -7px留空1px/-8px空隙还原 | -8px | -8px | 不兼容(仅Opera) |
14px/宋体 | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
13px/宋体 | -7px | -7px | -7px | -6px留空1px/-7px空隙还原 | -7px | -7px | 不兼容(仅Opera) |
12px/宋体 | -6px | -6px | -6px | -5px留空1px/-6px空隙还原 | -6px | -6px | 不兼容(仅Opera) |
16px/微软雅黑 | -5px | -5px | -5px | -4px留空1px/-5px空隙还原 | -5px | -5px | 不兼容(仅Opera) |
14px/微软雅黑 | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
13px/微软雅黑 | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
12px/微软雅黑 | -4px | -4px | -4px | -3px留空1px/-4px空隙还原 | -4px | -4px | 不兼容(仅Opera) |
您可以狠狠地点击这里: letter-spacing/空格间隙/字体/字体大小关系测试demo
可以看到,基本上所有的浏览器对于不同字体下的空格符的水平占据的解析都是一致,唯一有瑕疵的是在Opera浏览器下,两个inline-block元素间空白间隙使用letter-spacing去除的极限是1像素,当看上去要正好为0的时候,letter-spacing似乎失效,空白间距恢复成letter-spacing:0时的效果。
ok,现在我们把上面唠唠叨叨的些东西提炼整理一下,有如下一些结论:
» block水平的元素inline-block化后,IE6/7没有换行符间隙问题,其他浏览器均有;
» inline水平的元素inline-block后,所有主流浏览器都有换行符/空格间隙问题;
» font-size:0,去除换行符间隙,在IE6/7下残留1像素间隙,Chrome浏览器无效,其他浏览器都完美去除;
» letter-spacing负值可以去除所有浏览器的换行符间隙,但是,Opera浏览器下极限是间隙1像素,0像素会反弹,换行符间隙还原。
为什么袁隆平的水稻牛逼啊,就是因为是杂交的。这也是不宜近亲结婚之道,杂交可以产生彪悍的下一代。这个原理同样适用于CSS,虽然font-size:0有缺陷,letter-spacing负值也有不足,但是一旦将它们强制交配,哦呵呵,互补与整合,换行符间隙问题迎刃而解。如下测试代码(环境字体大小84%,字体Arial):
结果不支持font-size:0去间隙的Chrome浏览器:
letter-spacing无法实现完全去间隙的Opera浏览器:
您可以狠狠地点击这里: font-size:0,letter-spacing负值杂交demo
如果您使用的是IE6/7浏览器,可能会看到图片重叠了2像素,这是1像素文字间隙-3像素letter-spacing值的结果。但是,实际情况下,列表元素都是从block元素inline-block化的,元素间本身就没有间隙的,所以,就不会有重叠的问题了。
所以,应用display:inline-block属性实现列表布局的几个关键字就是: block水平的标签, font-size:0和 letter-spacing负值。
下面就将通过实例展现基于display:inline-block的列表布局。
七、基于display:inline-block的列表布局
本部分主要是实例展示,我比较喜欢顺手找素材做实例,而新浪微博又是我常常挂着的,所以,没有恶意的,又拿新浪微博做示例了。
目前新浪微博右侧的人气用户推荐列表是采用浮动布局的,现在,采用inline-block布局实现同样的,但是高度可不宜的布局:
您可以狠狠地点击这里: inline-block布局和浮动布局对比实例页面
页面的上面列表采用的是目前新浪微博的原CSS代码,而下面的列表就是使用的inline-block列表布局。相比前面的浮动布局,不必担心每个列表高度不一的问题,所以用户的姓名可以完整显示,这可比直接裁掉要好些。
demo页面中默认加载会有两条参考线,是由于应用了我自己写的jQuery标尺参考线插件,您有兴趣可以狠狠地点击这里: 网页制作辅助工具-jQuery标尺参考线插件
此插件会产生透明遮罩层,所以,你用firebug看代码的时候不能准确找到目标元素,此时只要按下”ESC”快捷键就可以隐藏标尺与参考线了。如果要想显示标尺,按下快捷键”R”即可,具体使用还是参见上面给出的文章链接。
显示两条参考线是用来说明,使用inline-block列表布局是没有兼容性问题的,可以达到与float布局一样的像素级无差异布局。
此例子中,文字大小为12像素,字体环境为Arial字体,对照上面的letter-spacing间隙数据表可知,这里的letter-spacing值应该是-3像素,于是……
其实人人网上就已经应用了基于display:inline-block的列表布局,您可以去看人人网的分享页面,其热门分享视频列表就是采用的基于display:inline-block的列表布局,但是其布局仅仅采用了letter-spacing负值,这也就意味着在Opera浏览器下,此列表是有兼容性问题的,如下面在Opera 10上的截图:
其他浏览器列表间距是12像素,而Opera下却是15像素,这是Opera浏览器letter-spacing负值反弹结果。但是,考虑到国情:Opera用户寥寥无几,所以,此不兼容性问题的影响也甚小。所以,您如果也不把Opera浏览器放在考虑范围内,使用inline-block列表布局时只要一个辅助CSS letter-spacing就可以了,如果顾虑到Opera,则不要忘了font-size:0;
八、更进一步:更加灵活的inline-block列表布局
如果你以为基于display:inline-block的列表布局的优点仅仅在于可以让列表元素不等高,那你就大错特错了。
在IE6/7下,inline水平标签inline-block化后与纯正的inline-block元素的作用就像是一个模子里出来的,这种“兼容性”可以很好地发挥inline-block列表布局的潜力。例如,使用white-space:nowrap;属性可以让列表不换行,你是否想到了列表元素的水平滚动切换?
使用text-align:justify可以实现自动等宽水平排列的列表布局,而且是两端对齐的,不需要计算宽度,一切都是浏览器自动的,很方便很强大。尤其在自适应布局中,大显身手,大放光彩。就以此举个简单的例子吧,如下CSS代码:
.box{width:50%; padding:20px; margin:20px auto; background-color:#f0f3f9; text-align:justify;}.list{width:120px; display:inline-block; padding-bottom:20px; text-align:center; vertical-align:top;}
如下HTML代码:
效果参见下面的视频(录制的是改变浏览器宽度时的效果):
从视频中可以看出,当浏览器宽度改变时,只要列表元素满行,inline-block化的列表都是等宽且两端对齐排列的,您可以狠狠地点击这里: 列表自动两端对齐排列demo
IE6/7下inline水平的元素模仿inline-block属性就算再像还是模仿,MJ还是MJ,小月月依旧是小月月,所以IE6/7下,像是text-justify两端对齐还是比较脆弱的,像是一不留神的标签,或是换行符都会导致失效,远不如纯正的inline-block属性来得威武 与坚挺。
九、啊,终于,结语
固定布局还是流体布局,本身网页重构的思想决定了该使用哪种列表布局方式。像我,就是个流体布局控,所以,我会直接挥挥手,跟浮动列表布局说拜拜的,翻译成日语就是:赛有拉拉!
本文仅是展示,如果您对浮动列表布局有些依依不舍的情怀,我会当大街上看到情侣打kiss一样,因为亲的又不是我女朋友(虽然没有),所以关我鸟事,你随意。好了,写了已经够多了,一些调侃的话就不说了。最后,毕竟资质有限,文中难免有表述不当的地方,欢迎指正,感谢阅读,就这些。