上一篇文章《圣杯布局小结》总结了几种常见的分栏布局方法,这几个方法均可以实现多栏页面下,全部栏的高度可动态变化,某一栏宽度自适应的布局效果,能知足工做中不少布局需求。后来我在搜集更多关于分栏布局的文章时,发现了一个新的问题,这个问题在前面那篇文章中也有朋友在评论里跟我提起,就是如何在实现分栏布局的同时保证每栏的高度相同。我发现这种等高分栏布局的状况,在网站里面其实也很常见,因此本文总结了几种可用的方法来解决这个新的需求。css
跟上篇文章不一样,此次把flex这种方法放在了第一位,由于相比较起来,它是全部分栏布局方法里面,优势最多的,若是兼容性容许的话,颇有必要在任什么时候候都优先使用它完成页面布局。若是你打开上篇文章,找到倒数第二部分关于flex实现分栏布局的代码,或者把上篇文章提供的代码下载下来,直接预览flex_layout.html,你会发现上篇文章的那段代码其实已经作到了等高分栏布局,同一段代码,能够实现上篇文章中提到的五种分栏布局,还能够实现本文提到的等高布局的状况,这种能力其它方法真的没法比拟。而它之因此能实现等高布局,跟一个flex的css属性有关系,这个属性是:align-item。它的默认值是:stretch,在flex item元素好比layout__main或layout__aside的高度未定义或者为auto的状况下,会拉伸flex item元素的高度或宽度,铺满flex的交叉轴,详细的原理能够经过上文提供的flex学习资源去了解,这里只作一个简单的引用说明。html
上篇文章中还有另外两种布局方法没有介绍,第一种就是这里要说的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相关的知识:web
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有效。app
3)关于table-cell还有一点要说明的就是,它会被其余一些CSS属性破坏,例如float, position:absolute,因此这些个属性不能同时使用。ide
4)跟直接使用表格元素不一样的是,在使用表格元素的时候须要彻底遵照表格元素嵌套结构,也就是下面这种:工具
<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的元素:布局
.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>
可是看到的效果是(蓝色背景是它们父层的一个包裹元素: width: 800px;margin-left: auto;margin-right: auto):学习
由于浏览器自动在这两个元素的外层,加了跟可以跟tr和table起相同做用的框,来包含这两个元素造成的框,因此这两个元素看起来就跟实际的表格效果同样。假如浏览器没有作这个处理,这两个元素之间是不可能没有间隙的,中间会有一个由于换行符显示出来的空格。这种自动添加的框都是行内框,不是块级框。flex
接下来看看如何经过这些伪table的属性来完成上文的分栏布局以及本文要求的等高分栏布局,玩法有不少:(本文相关源码下载)
玩法一:模拟直接用表格布局(对应源码中table_layout1.html)
这种方法的思路是布局时彻底按照表格的嵌套层次来处理,把display: table, display: table-row, display: table-cell都用上,至关于就是利用完整的table来作,好比说要实现上文的布局三(3栏布局,2个侧边栏分别固定在左边和右边,中间是主体内容栏),就能够这么干:
<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>
<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彻底去掉,一点都不影响布局效果:
<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>
<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这一个属性给去掉,反正浏览器还会再添加框来包裹:
<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>
<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稍加改动:
.layout__main {
width: 3000px;
background-color: #4DBCB0;
}
.layout__aside {
width: 200px;
min-width: 200px;
background-color: #daf1ef;
}
关键的代码就是红色新增的那两行,首先给主体内容栏设置一个很长的宽度,并且只能用具体的长度设置,不能用百分比,而后给侧边栏设置一个最小宽度,省得主体内容栏把侧边栏的宽度给挤掉了。这个原理就是由于display: table-cell的做用,致使layout__main跟layout__aside表现出跟td元素同样的特性,td默认的宽度就是可自动调整的,即便宽度设置的很大,也不会撑破table的宽度,这里虽然那个自动添加的框看不到,可是这个框的最大宽度也就是浏览器的宽度,layout__main不会打破这个宽度的,因此能够放心使用。
玩法四:去掉layout这一层包裹元素(对应源码:table_layout4.html)
若是网站比较简单,去掉layout这一层包裹元素也是能够的:
<header>顶部</header> <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> <footer>底部</footer>
<style type="text/css"> .layout__col { text-align: center; display: table-cell; line-height: 50px; } .layout__col + .layout__col { border-left: 10px solid #fff; } .layout__main { width: 3000px; background-color: #4DBCB0; } .layout__aside { width: 200px; min-width: 200px; background-color: #daf1ef; } </style>
以上四种作法都能实现咱们想要的分栏等高布局,兼容性方面,不考虑IE8及如下,其它浏览器几乎没有问题。
因为匿名表格的做用,致使采用伪table布局的方法变得很是简洁,上文之因此没提到这个作法,是由于彻底不知道有匿名表格这回事,我也是写这篇文章才学习到的,学完以后,发现又找到了一个作分栏布局的好办法,但愿前面的这些介绍能帮助你掌握好这个用法。实际上伪table的这些属性,尤为是table-cell,用途很是多,本文没有办法一一介绍,可是能提供一个思路,未来工做中也许有不少其它布局场景,咱们均可以想一想用table-cell来处理。
上文没有介绍的另一种分栏布局方法就是这里要介绍的绝对定位。之因此没介绍这个方法,是由于上文介绍的都是分栏自适应布局的方法,而绝对定位的作法,不能彻底作到咱们想要的分栏自适应布局,分栏自适应有两个原则:第一是主体内容栏宽度自适应,这点绝对定位是能够作到的;第二点是全部栏的高度都能动态变化,而且不能致使父容器高度塌陷,不能在各栏内部出现滚动或溢出的状况,这点绝对定位不容易作到适用全部场景。而本文又把这种布局方法拿出来介绍,是由于绝对定位作等高布局很容易,因此用绝对定位作等高分栏布局是一种可行的办法,只是这种方法适用的场景有一些限制,须要根据实际状况考虑是否要采用。
作法一:全部栏都采用绝对定位(对应源码中absolute_layout1.html)
<header>顶部</header> <div class="layout"> <aside class="layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__main">内容栏宽度自适应</div> <aside class="layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div> <footer>底部</footer>
<style type="text/css"> .layout { height: 300px; position: relative; } .layout__aside, .layout__main { position: absolute; top: 0; bottom: 0; } .layout__main { left: 210px; right: 210px; } .layout__aside { width: 200px; } .layout__aside--left { left: 0; } .layout__aside--right { right: 0; } </style>
效果:
这种布局方法的特色是:
1)主体内容栏是自适应的;
2)全部栏彻底等高,效果跟flex布局和伪table布局的效果同样;
从这两点来看,这种绝对定位的方法仍是比较好用的,不过它有一个很是大的使用限制,就是父元素的高度没有办法经过它的内部元素给撑起来,要用的话,必须想办法让父元素有高度,适合作父元素高度可知或者全屏布局。好比如下这个代码就是全屏布局的一个例子(对应源码中absolute_layout2.html):
<header>顶部</header> <div class="layout"> <aside class="layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__main">内容栏宽度自适应</div> <aside class="layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div> <footer>底部</footer>
<style type="text/css"> html,body { margin: 0; height: 100%; } footer { position: absolute; bottom: 0; width: 100%; } .layout { width: 100%; position: absolute; top: 50px; bottom: 50px; } .layout__aside, .layout__main { position: absolute; top: 0; bottom: 0; } .layout__main { left: 210px; right: 210px; } .layout__aside { width: 200px; } .layout__aside--left { left: 0; } .layout__aside--right { right: 0; } </style>
效果:
作法二:侧边栏绝对定位,主体内容栏保持流式布局(对应源码中absolute_layout3.html)
<div class="layout"> <aside class="layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__main">内容栏宽度自适应<br>高度增长一点,旁边的高度都会自动增长</div> <aside class="layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div>
<style type="text/css"> .layout { position: relative; } .layout__aside { position: absolute; top: 0; bottom: 0; } .layout__main { margin: 0 210px; } .layout__aside { width: 200px; } .layout__aside--left { left: 0; } .layout__aside--right { right: 0; } </style>
效果:
这个方法的特色是:
1)主体内容栏是宽度自适应的;
2)全部栏也是彻底等高的;
上面的代码中,layout__main经过magin来给侧边栏留出空间,其实也能够在layout元素上添加padding来处理,做用是同样的。这个方法相比前一个方法好一点的是,父元素的高度能够经过主体内容栏给撑起来,不过由此也带来了一个新问题,就是内容栏高度不够的时候,侧边栏就会出现溢出或者滚动,解决这个新问题的办法有2个:第一,若是侧边栏的内容都是已知的,而且没有折叠展开这种会改变侧边栏内容高度的功能,那么能够给layout设置一个min-height来处理;第二,若是侧边栏的内容是动态的,除了给layout加min-height以外,还得在每次改变侧边栏内容的时候,主动去调整主体内容栏的高度,若是主体内容栏的高度小于侧边栏的高度,就要更新主体内容栏的高度。不过若是你的内容栏的内容不少,侧边栏内容较少的话,就不用考虑这个新问题了。
绝对定位的作法就是这样,第一种限制较高;第二种稍微强一些,在一些场景下,可能还得借助JS来处理,因此综合起来不算是一个很是好的方式。只有你的布局需求刚好知足它的条件时,可能才会考虑使用它,就像上文中我提出的项目一的需求,就必定要用绝对定位的布局来作。
前面介绍了几种分栏等高布局,有table布局,伪table布局,绝对定位布局,flex布局,这四种布局方法在实现等高布局时,属于彻底等高的状况,就是说他们布局出来的页面,各栏的真实高度都是相同的,而且在任意栏的内容动态变化时,其它栏的高度都能相应地自动调整,若是布局的时候用的是这几个布局方法,那么等高的问题就不存在了。不过回看一下上文内容的话,上文提到的3种布局方式:圣杯布局,双飞翼布局,float布局,不用JS的话,就没法作到这种彻底等高的效果。这三种布局,只能考虑借助边框和背景实现视觉上的等高,也就是假等高的作法。毕竟从效果上来讲,若是没有设置背景和边框的话,即便是彻底等高,视觉上也看不出来,因此假等高的作法是值得采用的。
作法一:利用背景图片
以布局容器宽度固定的左中右三栏布局说明这个作法的步骤,首先制做一张高度较小,宽度跟布局容器宽度相同的背景图片,把这张图片做为布局容器的背景图垂直平铺。这张背景图要求跟页面同样也是分栏,并且每栏的宽度和栏之间的间隔都跟页面布局里面的栏位宽度和栏位间隔相同,这样才能保证,背景图片的每一个栏位与页面里面的每一个栏位重合。由于页面里面的每一个栏位底下,都有一个背景图片的栏位跟它对应,因此即便某一栏高度不够,可是视觉上这个栏位的高度跟布局容器的高度是相同的,这就是借助背景图来达到视觉等高的原理。这个作法的经典例子就是http://www.w3school.com.cn/,它的布局效果是这样的:
看起来这是一个三栏等高布局,分栏是经过float实现的,等高却不是彻底等高,而是经过背景图片实现的,它在布局容器上用了下面这张背景图:
它的布局html结构是(马赛克掉的是header,导航栏,footer,跟分栏布局没有关系,因此注掉了):
而后各栏只要都向左浮动,配置好宽度就行:
前面说明这个作法的步骤包括举的例子都针对的是布局容器宽度固定的左中右三栏布局,若是是自适应的分栏布局,又该怎么处理?下面以上文圣杯布局的布局三如何借助背景图片作到等高效果来讲明(为了减小篇幅,其它四种布局不会一一说明,可是这五种布局在源码中都有demo页面可查看,分别对应grail_bg_layout{1,5}.html)。
布局三:3栏布局,2个侧边栏分别固定在左边和右边,中间是主体内容栏:
<div class="layout--wrapper"> <div class="layout"> <aside class="layout__aside layout__aside--left">左侧边栏宽度固定</aside> <div class="layout__main">内容栏宽度自适应<br>高度增长一点,旁边的高度看起来都跟内容栏高度同样</div> <aside class="layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div> </div>
<style type="text/css"> .layout:after { content: " "; clear: both; display: table; } .layout__aside, .layout__main { float: left; height: 100%; } .layout--wrapper { background: url(aside_left.png) left top repeat-y #4DBCB0; } .layout { background: url(aside_right.png) right top repeat-y; padding:0 210px; } .layout__main { width: 100%; } .layout__aside { width: 200px; } .layout__aside--left { margin-left: -210px; } .layout__aside--right { margin-right: -210px; float: right; } </style>
效果:
实现这个等高效果的关键是:
1)去掉在layout__aside layout__main上设置的背景色;
2)制做2张背景图片,宽度都是210 * 10,分别用来作两个侧边栏的背景:
3) html结构稍微调整下,加一层wrapper
4)layout--wrapper的背景以下设置:
background: url(aside_left.png) left top repeat-y #4DBCB0;
这样左侧边栏和内容栏就都有了背景,左侧边栏与内容栏之间的间隔效果也出来了。
5)layout的背景以下设置:
background: url(aside_right.png) right top repeat-y;
这样右侧边栏就有了背景,右侧边栏与内容栏之间的间隔效果也出来了。
以上就是利用背景图作假等高效果的所有内容,这个作法对于要用背景来呈现等高效果的布局是很是好用的一个方法,虽然网上都说它的缺点是用到了图片,布局一改,图片就要改,我我的认为这并非缺点,由于这样的需求变更,第一是没法避免,第二是变更地次数不必定不少,第三即便变了改起来也是两三分钟的事,要是本身会点PS,那弄起来就更简单了。能解决问题的简单方法就是最好的办法。
作法二: 利用边框重叠
首先得说这个作法,适合要用边框来呈现等高效果的布局,也就是说各栏不能有背景,不然看起来边框是等高了,可是背景没有等高。它的原理要分红两部分来讲,若是是2栏布局,作法比较简单,好比左右分栏的布局,给侧边栏加一个右边框,给内容栏加一个左边框,而后给内容栏加上负的margin-left,让2个边框重合,这样无论哪一个栏位内容多,边框重叠以后的高度就跟总体的高度一致了,也就达到了咱们想要的等高效果;若是是多栏布局,这种边框重叠的方式不能作到全部场景下的视觉等高,好比说左中右三栏布局,如今是按边框重叠的办法实现了下等高,当左边栏的内容动态增长不少时,右边侧边栏跟内容栏的边框总体高度,并不会动态增长,而后就会造成错层的效果,对于这种状况,能够利用绝对定位,用额外的空元素模拟栏与栏之间的边框效果。
下面以上文圣杯布局的布局一和布局三如何借助边框重叠和模拟作到等高效果来讲明(为了减小篇幅,其它布局不会一一说明,可是这几种布局在源码中都有demo页面可查看,分别对应grail_border_layout{1,3}.html)。
1)布局一:2栏布局,侧边栏固定在左边,右侧是主体内容栏:
<div class="layout"> <aside class="layout__aside">侧边栏宽度固定</aside> <div class="layout__main">内容栏宽度自适应<br>高度增长一点,旁边的高度看起来都跟内容栏高度同样</div> </div>
<style type="text/css"> .layout:after { content: " "; clear: both; display: table; } .layout__aside, .layout__main { float: left; } .layout { padding-left: 201px; } .layout__main { width: 100%; margin-left: -1px; border-left: 1px solid #ccc; } .layout__aside { width: 200px; border-right: 1px solid #ccc; margin-left: -201px; } </style>
要点是:
1)要从新调整layout的padding值,和layout__aside的margin值,栏与栏之间不能有间隔;
2)layout__main设置-1px的margin-left和1px的border-left;layout__aside设置1px的border-right
效果:
布局三:3栏布局,2个侧边栏分别固定在左边和右边,中间是主体内容栏:
<div class="layout"> <aside class="layout__division layout__division--left"></aside> <aside class="layout__division layout__division--right"></aside> <aside class="layout__aside layout__aside--left">左侧边栏宽度固定<br>再加点东西<br>再加点东西</aside> <div class="layout__main">内容栏宽度自适应<br>高度增长一点</div> <aside class="layout__aside layout__aside--right">右侧边栏宽度固定</aside> </div>
<style type="text/css"> .layout:after { content: " "; clear: both; display: table; } .layout__aside, .layout__main { float: left; height: 100%; } .layout { padding:0 201px; position: relative; } .layout__main { width: 100%; } .layout__aside { width: 200px; } .layout__aside--left { margin-left: -201px; } .layout__aside--right { margin-right: -201px; float: right; } .layout__division { position: absolute; border-left: 1px solid #ccc; height: 100%; } .layout__division--left { left:200px; } .layout__division--right { right:200px; } </style>
效果:
布局三这个作法的原理比真实的边框重叠还要简单些,可是不如那种方法简洁,毕竟要增长没有用的html元素,因此不算是一个好方法。
最后综合比较背景和边框这两种假等高作法,更值得推荐的是作法一,边框这种效果,经过背景图也是能够作出来的,并且边框可以实现的视觉效果有限,利用边框的多栏等高布局还得增长冗余的HTML元素,缺陷比较明显。
本文介绍了4种能够作到彻底等高的分栏自适应布局方法,对于上文的几种分栏布局:圣杯布局,双飞翼布局,float布局,也提供了一个简单高效的方法能够作到视觉上的假等高效果,这些方法都是可靠有效的,只要知足本身的工做需求,喜欢用哪一种就用哪一种,毕竟工做的目的是完成工做目标,而不是尝试哪一种工具好用。但若是公司的产品不考虑那些陈旧的浏览器的话,我以为全部的布局只须要三个方法:flex,table-cell和position,其它的圣杯布局,双飞翼布局,float布局就让它成为经典,留在本身的博客总结中就好。PS: 虽然上文我在推荐圣杯布局,可是我已经打算把个人项目二的布局方式换成table-cell来搞了。
本文内容繁多,相信耽误您很多时间,谢谢阅读,提早祝您新年快乐:)