上一篇文章《圣杯布局小结》总结了几种常见的分栏布局方法,这几个方法都可以实现多栏页面下,所有栏的高度可动态变化,某一栏宽度自适应的布局效果,能满足工作中很多布局需求。后来我在搜集更多关于分栏布局的文章时,发现了一个新的问题,这个问题在前面那篇文章中也有朋友在评论里跟我提起,就是如何在实现分栏布局的同时保证每栏的高度相同。我发现这种等高分栏布局的情况,在网站里面其实也很常见,所以本文总结了几种可用的方法来解决这个新的需求。
1. 方法一:万能的flex
跟上篇文章不同,这次把flex这种方法放在了第一位,因为相比较起来,它是所有分栏布局方法里面,优点最多的,如果兼容性允许的话,很有必要在任何时候都优先使用它完成页面布局。如果你打开上篇文章,找到倒数第二部分关于flex实现分栏布局的代码,或者把上篇文章提供的代码下载下来,直接预览flex_layout.html,你会发现上篇文章的那段代码其实已经做到了等高分栏布局,同一段代码,可以实现上篇文章中提到的五种分栏布局,还可以实现本文提到的等高布局的情况,这种能力其它方法真的无法比拟。而它之所以能实现等高布局,跟一个flex的css属性有关系,这个属性是:align-item。它的默认值是:stretch,在flex item元素比如layout__main或layout__aside的高度未定义或者为auto的情况下,会拉伸flex item元素的高度或宽度,铺满flex的交叉轴,详细的原理可以通过上文提供的flex学习资源去了解,这里只做一个简单的引用说明。
2. 方法二:使用table或者伪table
上篇文章中还有另外两种布局方法没有介绍,第一种就是这里要说的table布局或者伪table布局。table布局用的就是table tr td这些元素去实现,相信绝大部分web开发人员在入门html时,首先接触到的布局方法肯定就是table布局了,这种方法简单高效,用它做任何分栏布局都不是问题,只是因为table的嵌套结构太多,html冗杂,又不利于DOM的操作和渲染,用来布局不符合语义,总之缺点较多,所以目前的环境下,用的情况越来越少了。伪table布局其实跟table布局类似,只不过借助于css,可以让我们不直接使用table tr td这些直接的表格元素,而是通过display: table, display: table-row, display: table-cell,改变元素的显示特性,让浏览器把这些元素当成table来渲染,这种渲染的表现跟用真实的table没有啥区别,就连那些table专用的css属性,比如table-layout,border-collapse和border-spacing,都能产生效果。table布局的方法已经很少被采用了,本文也就没必要再去介绍,但是伪table布局的方法值得学习一下,经过这两天的学习,发现伪table的方式相比直接用表格布局,有不少的优点,值得运用到工作中去。不过在说明使用伪table布局的方法之前,得先了解一些伪table相关的知识:
1)可用于伪table表现的display属性值有:
2)当把一个元素的display属性设置成以上列出的值后,就可以把这个元素看成与该属性对应的表格元素,比如table-cell对应的就是td;同时,这个元素会拥有跟表格元素一样的特性,比如display: table或者inline-table的元素可以使用table-layout,border-collapse和border-spacing这三个原本只有table才能生效的属性;display:table-cell的元素跟td一样,对宽度高度敏感,对margin值无反应,对padding有效。
3)关于table-cell还有一点要说明的就是,它会被其他一些CSS属性破坏,例如float, position:absolute,所以这些个属性不能同时使用。
4)跟直接使用表格元素不同的是,在使用表格元素的时候需要完全遵守表格元素嵌套结构,也就是下面这种:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<table> <thead> <th></th> </thead> <tbody> <tr> <td></td> </tr> </tbody> <tfoot> <th></th> </tfoot> </table> |
而使用伪table的那些属性时,可以仅单独使用某一个属性,浏览器会在这些元素的外层包裹缺失的框来保证伪table元素框嵌套结构的完整性,这些框跟常提到的行框一样都是不可见的,网上有的文章里也把这种做法叫做匿名表格。下面的这个代码中,tb-cell元素的外层没有加display: table-row和display: table的元素:
1 2 3 4 5 6 7 8 |
.tb-cell { display: table-cell; padding: 10px; border: 1px solid #ccc; } <div class="tb-cell">这是第1个display: table-cell;的元素。</div> <div class="tb-cell">这是第2个display: table-cell;的元素。</div> |
1 |
这是第1个display: table-cell;的元素。这是第2个display: table-cell;的元素。 |
但是看到的效果是(蓝色背景是它们父层的一个包裹元素: width: 800px;margin-left: auto;margin-right: auto):
因为浏览器自动在这两个元素的外层,加了跟能够跟tr和table起相同作用的框,来包含这两个元素形成的框,所以这两个元素看起来就跟实际的表格效果一样。假如浏览器没有做这个处理,这两个元素之间是不可能没有间隙的,中间会有一个因为换行符显示出来的空格。这种自动添加的框都是行内框,不是块级框。
接下来看看如何通过这些伪table的属性来完成上文的分栏布局以及本文要求的等高分栏布局,玩法有很多:(本文相关源码下载)
玩法一:模拟直接用表格布局(对应源码中table_layout1.html)
这种方法的思路是布局时完全按照表格的嵌套层次来处理,把display: table, display: table-row, display: table-cell都用上,相当于就是利用完整的table来做,比如说要实现上文的布局三(3栏布局,2个侧边栏分别固定在左边和右边,中间是主体内容栏),就可以这么干:
1 2 3 4 5 6 7 |
<div class="layout"> <div class="layout__row"> <aside class="layout__col layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__col layout__main">内容栏宽度自适应<br>高度增加一点,旁边的高度都会自动增加</div> <aside class="layout__col layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<style type="text/css"> .layout { display: table; width: 100%; } .layout__row { display: table-row; } .layout__col { text-align: center; display: table-cell; } .layout__col + .layout__col { border-left: 10px solid #fff; } .layout__main { background-color: #4DBCB0; } .layout__aside { width: 200px; background-color: #daf1ef; } </style> |
效果还是那个效果,而且天生支持等高布局:
这个布局原理跟使用table是完全一样的,所以使用起来非常容易(以上提供的是针对上文布局三的实现,其它四个布局的实现不会再一一介绍了,源码里面也不会提供,因为相对比较简单)。
这种伪table布局有什么特点呢:
1)相比直接用表格元素,这种做法不需要考虑语义,表格元素是有语义的,主要是用来显示网页上列表型的数据内容,虽然可以完成布局,但是布局结构都是没有语义的,所以直接用表格不合适,而这种伪table布局的特点就是:它没有语义,但是可以像表格那样布局;
2)html的层次结构相比直接用table元素也要简单一些,我们这里只用到了3层,直接用table元素的话可能还有tbody这一层;
3)相比上文提到的那些布局方法,如圣杯布局和双飞翼布局,这个做法在css方面相对简单,在html方面也只多了一层嵌套;
4)缺点是分栏之间的间隔不能用margin和padding来做,如果用margin,这个属性在display: table-cell的元素上根本不会生效;如果用padding,那像demo里面各栏的背景色就都会连到一块,做不出间隔的效果,如果在layout__col里面再嵌套一层,在这一层设置背景色的话,又会增加html的层次,也不是很好。我这里是投了个巧,用border处理了一下。
玩法二:去掉display: table-row(对应源码中的table_layout2.html)
前面说过,浏览器会用匿名表格的方式,添加缺失的框,所以玩法一中的代码,把layout-row完全去掉,一点都不影响布局效果:
1 2 3 4 5 |
div class="layout"> aside class="layout__col layout__aside layout__aside--left">左侧边栏宽度固定aside> div class="layout__col layout__main">内容栏宽度自适应br>高度增加一点,旁边的高度都会自动增加div> aside class="layout__col layout__aside layout__aside--right">右侧边栏宽度固定aside> div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
style type="text/css"> .layout { display: table; width: 100%; } .layout__col { text-align: center; display: table-cell; } .layout__col + .layout__col { border-left: 10px solid #fff; } .layout__main { background-color: #4DBCB0; } .layout__aside { width: 200px; background-color: #daf1ef; } style> |
玩法三:去掉display: table(对应源码中的table_layout3.html)
根据玩法二,可以试想一下是否能再把display: table这一个属性给去掉,反正浏览器还会再添加框来包裹:
1 2 3 4 5 |
<div class="layout"> <aside class="layout__col layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__col layout__main">内容栏宽度自适应<br>高度增加一点,旁边的高度都会自动增加</div> <aside class="layout__col layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<style type="text/css"> .layout__col { text-align: center; display: table-cell; } .layout__col + .layout__col { border-left: 10px solid #fff; } .layout__main { background-color: #4DBCB0; } .layout__aside { width: 200px; min-width: 200px; background-color: #daf1ef; } </style> |
效果是:
这个并没有达到我们的效果,因为我需要主体内容栏能够自适应宽度。产生这个效果的原因是什么,就是因为没有加显示display: table这一层,浏览器自动加了一个框,不过这个框是行内框,导致主体内容栏显示的宽度就跟内容的宽度一致了。为了解决这个问题,可以这么干,html结构不变,css稍加改动: