5.3 三栏-中栏流动布局

实现中栏流动布局有两种方法。一种是在中栏改变大小时使用负外边距定位右栏,另一种是使用CSS3让栏容器具有类似表格单元的行为。负外边距适合比较老的浏览器,而CSS的table属性则要简单得多。本节介绍这两种方法。

5.3.1 用负外边距实现

实现三栏布局且让中栏内容区流动(不固定)的核心问题,就是处理右栏的定位,并在中栏内容区大小改变时控制右栏与布局的关系。

Web开发人员Ryan Brill给出的解决方案是控制两个外包装(通过ID值为wrapper)容器的外边距。其中一个外包装包围所有三栏,另一个外包装只包围左栏和中栏。由于相应的标记和CSS与我们前面刚讲过的固定栏宽布局相似,所以有些细节就没有必要再重复了。不过,以下代码加粗了相应的外包装元素及CSS规则,并配上了简明的注释,最后还给出了两个屏幕截图(图5-10a和b)。

  1. <div id="main_wrapper">
  2. <header>
  3. <!-- 页眉-->
  4. </header>
  5. <div id="threecolwrap">/*三栏外包装(包围全部三栏)*/
  6. <div id="twocolwrap">/*两栏外包装(包围左栏和中栏)*/
  7. /*左栏*/
  8. <nav>
  9. <!-- 导航 -->
  10. </nav>
  11. /*中栏*/
  12. <article>
  13. <!-- 区块 -->
  14. </article>
  15. </div>/*结束两栏外包装(twocolwrap)*/
  16. /*右栏*/
  17. <aside>
  18. <!-- 侧栏 -->
  19. </aside>
  20. </div>/*结束三栏外包装(threecolwrap)*/
  21. <footer>
  22. <!-- 页脚 -->
  23. </footer>
  24. </div>

以下就是CSS规则。

下载的代码中包含一些与修复IE6和IE7的bug有关的代码,这些代码都有注释。

  1. * {margin:0; padding:0;}
  2. body {font:1em helvetica, arial, sans-serif;}
  3. div#main_wrapper{
  4. min-width:600px; max-width:1100px;
  5. /*超过最大宽度时,居中布局*/
  6. margin:0 auto;
  7. /*背景图片默认从左上角开始拼接*/
  8. background:url(images/bg_tile_150pxw.png) repeat-y #eee;
  9. }
  10. header {
  11. padding:5px 10px;
  12. background:#3f7ccf;
  13. }
  14. div#threecolwrap {
  15. /*浮动强制它包围浮动的栏*/
  16. float:left;
  17. width:100%;
  18. /*背景图片右对齐*/
  19. background:url(images/bg_tile_210pxw.png) top right repeat-y;
  20. }
  21. div#twocolwrap {
  22. /*浮动强制它包围浮动的栏*/
  23. float:left;
  24. width:100%;
  25. /*把右栏拉到区块外边距腾出的位置上*/
  26. margin-right:-210px;
  27. }
  28. nav {
  29. float:left;
  30. width:150px;
  31. background:#f00;
  32. padding:20px 0;
  33. }
  34. /*让子元素与栏边界保持一定距离*/
  35. nav > * {margin:0 10px;}
  36. article {
  37. width:auto;
  38. margin-left:150px;
  39. /*在流动居中的栏右侧腾出空间*/
  40. margin-right:210px;
  41. background:#eee;
  42. padding:20px 0;
  43. }
  44. *让子元素与栏边界保持一定距离*/
  45. article > * {margin:0 20px;}
  46. aside {
  47. float:left;
  48. width:210px;
  49. background:#ffed53;
  50. padding:20px 0;
  51. }
  52. *让子元素与栏边界保持一定距离*/
  53. aside > * {margin:0 10px;}
  54. footer {
  55. clear:both;
  56. width:100%;
  57. text-align:center;
  58. background:#000;
  59. }

enter image description here图5-10a 屏幕变宽时,中栏变宽,左栏和右栏宽度不变

enter image description here图5-10b 屏幕变窄时,中栏变窄,左栏和右栏宽度不变

图5-10a和b展示了流动中栏布局。下面简单说明其原理。三栏中的右栏是210像素宽。为了给右栏腾出空间,中栏article元素有一个210像素的右外边距。当然,光有这个外边距只能把右栏再向右推210像素。别急,包围左栏和中栏的两栏外包装上210像素的负右外边距,会把右栏拉回article元素右外边距(在两栏外包装内部右侧)创造的空间内。中栏aticle元素的宽度是auto(原文100%有错误。——译者注),因此它仍然会力求占据浮动左栏剩余的所有空间。可是,一方面它自己的右外边距在两栏外包装内为右栏腾出了空间,另一方面两栏外包装的负右外边距又把右栏拉到了该空间内。总之,这是个很巧妙的设计。

人造栏技术

有人可能会纳闷,这些栏怎么都跟布局一样高呢?实话跟你说吧,你看到的都是假象!这里我采用了一种叫“人造栏”的技术,这样才让所有栏看起来都一样高了。这个技术说来也简单,就是给包围栏的外包装元素应用与各栏同宽的背景图片和背景色。外包装元素跟它们包含的栏不一样,它们的高度就是布局高度,当然与内容区的高度相同。通过在它们的背景的垂直方向上重复拼接背景图片,就可以在视觉上造成各栏与布局同高的假象。这个例子分别为左栏和右栏应用了不同的背景图片(如图5-11所示),为流动的中栏应用了背景色。

enter image description here图5-11 为流动中栏布局的左栏和右栏准备的两张背景图片

如前面CSS加粗的代码所示,左栏的背景图片加在了div#main_wrapper上。右栏的背景图片加在了div#threecolwrap上,而且让它沿该div的右侧垂直拼接。中栏的背景色也加在了div#main_wrapper上,这个背景色实际上是整个布局的背景色。而位于两侧的两栏的背景图片,以及页眉和页脚的背景色,都会覆盖这个背景色(子元素覆盖父元素)。因此,只能在中栏看到该全局背景色。

访问本书网站http://www.stylinwithcss.com,可以免费阅读使用jQuery和渐变图生成人造栏的一章。

在这个页面布局的例子中,还有一个知识点值得注意。那就是我使用“子-星选择符”为内容元素添加了水平外边距(如nav > * {margin:0 10px;}),而没有采用给内部div添加内边距的方法。这样就让你在实际案例中看到该选择符的应用。要了解“子-星选择符”的细节,请参考5.2节中“为栏设定内边距和边框”小节。

5.3.2 用CSS3单元格实现

尽管利用HTML的<table>标签实现多栏布局是难以接受的,但使用CSS让布局形如表格则是绝对可以接受的。这种方法不会导致固定不变的表格布局,也不会出现难以重新应用样式的问题(比如在手持设备上表现为一栏)。说到创建布局,表格的行为确实是非常符合要求的,下面我来解释一下。

在最简单的情况下,表格由三个元素构成。一个表格外包装<table>,包含着表格行<tr>和表格数据<td>,比如下面这个例子。

  1. <table>
  2. <tr> <td>Cell 1</td><td>Cell 2</td><td>Cell 3</td> </tr>
  3. <tr> <td>Cell 1</td><td>Cell 2</td><td>Cell 3</td> </tr>
  4. </table>

我们知道,CSS可以把一个HTML元素的display属性设定为tabletable-rowtable-cell。通过这种方法可以模拟相应HTML元素的行为。而通过CSS把布局中的栏设定为table-cell有三个好处。

  • 单元格(table-cell)不需要浮动就可以并排显示,而且直接为它们应用内边距也不会破坏布局。
  • 默认情况下,一行中的所有单元格高度相同,因而也不需要人造的等高栏效果了。
  • 任何没有明确设定宽度的栏都是流动的。这三个好处解决了本章前面学习布局时遇到的问题。然而,(这里一定有蹊跷,对吧?)CSS3表格行为在IE7及更低版本中并没有得到支持,而且也没有稳妥的补救措施。如果你(或者你的客户)愿意摒弃IE7,那么它就是一个既简单又可靠,而且还很彻底的解决方案。如果真是这样,我绝对推荐你采用这个方案,前面所讲的各种方案就当我没说。

关键是,你甚至都不需要用div外包装来扮演tabletr元素,仅仅是把三栏的display属性设定为table-cell就可以了。浏览器的排版引擎在碰到没有表行(tr)的一组单元格时,会自动为它们添加表行,而在表行没有被table元素包围时,会自动为表行添加table。因此,你不需要多写任何标记,只要把每一栏的display属性设定为table-cell,剩下的事儿就可全部交给浏览器负责了。

因此,要实现一个三栏-流动中栏布局,只需要以下标记:

  1. <nav><!-- 内容 --></nav>
  2. <article><!-- 内容 --></article>
  3. <aside><!-- 内容 --></aside>

和以下CSS:

  1. nav {display:table-cell; width:150px; padding:10px;
  2. background:#dcd9c0;}
  3. article {display:table-cell; padding:10px 20px;
  4. background:#ffed53;}
  5. aside {display:table-cell; width:210px; padding:10px;
  6. background:#3f7ccf;}

在图5-5所示固定宽度布局的HTML基础上,我们去掉了内部div。而在CSS中,把每一栏的float:left替换成了display:table-cell,同时去掉了中栏的宽度设定。好了,一个中栏流动、各栏同高,而且能够方便为内容添加内边距和边框的布局就这么出炉了(参见图5-12)!

enter image description here图5-12 这个流动布局使用了CSS3的display:table-cell,让每个栏形同单元格一般

请注意,这个简单、功能完备的布局对IE7和IE6可没有任何腻子脚本,甚至连个退化的后备方案都没有。在这些浏览器中,三栏会上下堆叠在一起。因此,除非你下定决心不再支持老版本的IE,否则就得使用本章前面讲过的其他布局技术。等吧,等到这些浏览器没人用为止。

最后我们总结一下本节讨论的布局技术。这一节先讲了使用内部div的浮动固定宽度布局技术,它是适合新旧浏览器的一种最安全的技术。当然,这种技术要求的工作量也最多。而使用boxsizing:border-box声明(再加上适用于IE7和IE6的腻子脚本) 则能提供更直观的盒模型,而且不用使用内部div。最后,CSS3的display:table-cell方案容易实现又功能完善(想流动就流动,想固定就固定,各栏同高,而且不需要内部div)。然而,它只适合IE7以上版本的浏览器。

到目前为止,我们例子中的布局都是在页眉和页脚之间简单地放着三个栏。本章最后,我们在前面三栏布局的基础上,再介绍一个稍微复杂一些的例子。