流体宽度等高列

Avatar of Chris Coyier
Chris Coyier 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

等高列一直是网页设计师的需求。如果所有列共享相同的背景,则等高无关紧要,因为您可以将该背景设置为父元素。但是,如果一个或多个列需要具有自己的背景,那么它对于设计的视觉完整性变得非常重要。

问题:三列具有不同数量的内容,只会根据需要分别增长高度。
期望:所有列的高度都相等,与最高的列高度匹配。

如果设计是流体宽度,则此任务变得相当容易。对于固定宽度布局的最佳技术是 Dan Cederholm 的 伪列,其中列包装在容器元素中(您可能已经有了),并且该容器应用了垂直重复的图像背景,以模拟等高列的外观,即使元素本身实际上并没有增长。

流体宽度多列同时出现时,此任务变得更加困难。我们不能再使用静态图像来模拟多列的外观。但并非所有希望都破灭了。下面我们将研究一些完成流体宽度等高列的不同技术。

Doug Neiner 方法

Doug 在几个月前与我们进行的一次小型极客聊天中即兴想出了这个方法。其想法是使用 CSS3 渐变来创建列。渐变?!确实如此。我们通常认为渐变是颜色在距离上逐渐过渡到另一种颜色。但是,我们通过声明“颜色停止点”来使用 CSS 创建渐变,这些颜色停止点是颜色将完全按照该点指定的颜色显示的特定位置。诀窍是使用重叠的颜色停止点。这样,您就可以使一种颜色停止,另一种颜色开始,而不会出现任何可见的“渐变”。

看看如何通过在 0% 和 100% 标记处声明一个颜色停止点,并在 20%/40%/60%/80% 标记处声明两个颜色停止点,从而获得五列背景。

.five-columns {
  background-image: -webkit-gradient(linear,
    left top,
    right top,
    color-stop(0, #eee),
    color-stop(20%, #eee),
    color-stop(20%, #ccc),
    color-stop(40%, #ccc),
    color-stop(40%, #eee),
    color-stop(60%, #eee),
    color-stop(60%, #ccc),
    color-stop(80%, #ccc),
    color-stop(80%, #eee),
    color-stop(100%, #eee)
	);
  background-image: -webkit-linear-gradient(
    left,
    #eee,
    #eee 20%,
    #ccc 20%,
    #ccc 40%,
    #eee 40%,
    #eee 60%,
    #ccc 60%,
    #ccc 80%,
    #eee 80%,
    #eee 100%
	);
  background-image: -moz-linear-gradient(
    left,
    #eee,
    #eee 20%,
    #ccc 20%,
    #ccc 40%,
    #eee 40%,
    #eee 60%,
    #ccc 60%,
    #ccc 80%,
    #eee 80%,
    #eee 100%
	);
  background-image: -ms-linear-gradient(
    left,
    #eee,
    #eee 20%,
    #ccc 20%,
    #ccc 40%,
    #eee 40%,
    #eee 60%,
    #ccc 60%,
    #ccc 80%,
    #eee 80%,
    #eee 100%
	);
  background-image: -o-linear-gradient(
    left,
    #eee,
    #eee 20%,
    #ccc 20%,
    #ccc 40%,
    #eee 40%,
    #eee 60%,
    #ccc 60%,
    #ccc 80%,
    #eee 80%,
    #eee 100%
	);
}

此 CSS 应用于所有列的包装器。因为我们对这些颜色停止点使用百分比,所以此模拟的五列背景图像将像您在流体宽度设计中预期的那样伸展和收缩。您可以将其视为伪列的现代扩展。

标记本身是该包装器内的一系列列。

<div class="five-columns group">
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
</div>

注意“group”类,它是 clearfix 类,用于使父包装器即使包含仅浮动子元素也能保持其高度(否则它通常会折叠)。

我认为这是一个相当巧妙的想法。请注意,只有现代的 Gecko 和 WebKit 浏览器支持 CSS3 渐变,因此您的 Opera 和旧版 IE 访问者将看不到列背景。但是,即使在 IE 6 中,列结构也应保持完整。

此方法允许通过使用负和正左边距来调整列位置,从而实现源顺序独立性。这是一个 Doug Neiner 方法的演示,其中源顺序中第一个列被移动到中间。

Nicolas Gallagher 方法

Nicolas Gallagher 发布了 一篇精彩的文章,介绍了如何使用 CSS2 伪元素来实现许多难以实现或需要额外 HTML 杂乱的效果。

其想法是将父包装器设置为相对定位。这为内部的绝对定位设置了上下文。然后,我们将三个列中的每个列设置为父元素宽度的三分之一,并在其内部相对定位,根据需要使用相对定位将其推开。这也允许源顺序独立性。

两个可见的背景色列由绝对定位的块级伪元素(:before 和 :after)生成,这些伪元素再次是宽度的三分之一,但高度为父元素的 100%。这两个“列”具有负 z-index 值,因此它们可以位于列的可见文本内容下方。第三个“列”实际上只是包装器的背景颜色显示出来。由于包装器的高度将由最高的列的高度设置,因此这可以正常工作。

.pseudo-three-col {
  position: relative;
  background: #eee;
  z-index: 1;
  width: 100%;
}
.pseudo-three-col .col {
  position: relative;
  width: 27%;
  padding: 3%;
  float: left;
}
.pseudo-three-col .col:nth-child(1) { left: 33%; }
.pseudo-three-col .col:nth-child(2) { left: -33.3%; }
.pseudo-three-col .col:nth-child(3) { left: 0; }
.pseudo-three-col:before, .pseudo-three-col:after {
  content: " ";
  position: absolute;
  z-index: -1;
  top: 0;
  left: 33.4%;
  width: 33.4%;
  height: 100%;
  background: #ccc;
}
.pseudo-three-col:after {
  left: 66.667%;
  background: #eee;
}

使用表格

有时,一种经过验证且可靠的方法可以完成工作。确保实现流体宽度等高列的一种方法是使用一个表格单元格行。以防您忘记了它的外观,它看起来像这样

<table id="actual-table">
  <tr>
    <td>
      <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
    </td>
    <td>
      <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
    </td>
    <td>
      <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
    </td>
    <td>
      <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
    </td>
    <td>
      <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
    </td>
  </tr>
</table>

为每个表格单元格指定一个百分比宽度,使其总计为 100%,您就完成了。即使您随后向单元格应用填充,表格也会正确排列自身。

#actual-table { border-collapse: collapse; }
#actual-table td {
  width: 20%;
  padding: 10px;
  vertical-align: top;
}
#actual-table td:nth-child(even) {
  background: #ccc;
}
#actual-table td:nth-child(odd) {
  background: #eee;
}

现在假设该表格标记让您感到不适。您实际上可以使用普通的 div 标记,但仍然强制其表现得像表格一样。在这种情况下,我们将执行以下操作

<div id="css-table">
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
</div>

然后使用 CSS 表格

#css-table {
  display: table;
}
#css-table .col {
  display: table-cell;
  width: 25%;
  padding: 10px;
}
#css-table .col:nth-child(even) {
  background: #ccc;
}
#css-table .col:nth-child(odd) {
  background: #eee;
}

除了使用舒适的标记之外,我们实际上还节省了一些标记,因为我们可以直接从表格到表格单元格。没有元素需要模拟表格行。

那么 CSS 表格是我们梦想的答案吗?它们有点酷,但它们在 IE 7 中不受支持,因此如果您有兴趣走这条路,我建议您只使用实际的表格标记。使用 CSS 表格实际上没有任何明显的优势。

两种表格方法最显着的缺点是源顺序依赖性。实际上无法使源顺序中的第一列出现在第一列以外的任何位置。

唯一布局方法

有史以来最经典的布局之一是 唯一布局。在一个演示中,等高列得到了解决。这是一种相当巧妙的技术,至今仍然非常有效。其想法,像往常一样,是为所有列使用一个包装元素。此包装器设置为隐藏溢出,这不仅可以清除浮动列,还可以隐藏超出包装器范围的任何内容。这尤其重要,因为我们将强制列的高度非常高,然后使用隐藏溢出将其切掉。这里的魔法在于,虽然我们使用大量的底部填充来强制列更高,但我们使用等量的负底部边距来吸回包装器的高度。这正是我们需要的效果。

标记与我们之前看到的没有什么不同

<div id="one-true" class="group">
  <div class="col"><h3>I am listed first in source order.</h3><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
</div>

然后 CSS 只是浮动列以及边距/填充技巧。

#one-true { overflow: hidden; }
#one-true .col {
  width: 27%;
  padding: 30px 3.15% 0;
  float: left;
  margin-bottom: -99999px;
  padding-bottom: 99999px;
}
#one-true .col:nth-child(1) { margin-left: 33.3%; background: #ccc; }
#one-true .col:nth-child(2) { margin-left: -66.3%; background: #eee; }
#one-true .col:nth-child(3) { left: 0; background: #eee; }
#one-true p { margin-bottom: 30px; } /* Bottom padding on col is busy */

请注意,列底部的填充是由内部内容向下推生成的,因为我们不能依赖列本身的底部填充,因为它正忙于其花哨的技巧。这里的源顺序独立性与我们已经介绍过的相同,通过使用左边界进行调整。

Flexbox 方法

Flexbox 可以轻松处理这种情况。这是一个参考,其中包含所有功能和浏览器支持等。

标记再次非常干净

<div class="flexbox">
  <div class="col"><h3>I am listed first in source order.</h3><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
  <div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
</div>

CSS 需要一些供应商前缀(或使用 Autoprefixer)来启动 flex 布局

.flexbox {
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  overflow: hidden;
}

从那里,我们可以非常轻松地使它们的宽度相等

.flexbox .col {
  flex: 1;
}

可以将align-items属性设置为拉伸以确保它们等高,但这是默认值!因此我们根本不需要设置它。

最后保存最佳方案,我们可以通过将列的顺序设置为所需位置来更改列的位置。最好明确设置所有列的所需位置;我发现仅设置某些列存在问题。尽管如此,能够在没有布局技巧的情况下更改位置仍然很棒。

.flexbox .col:nth-child(1) {
  background: #ccc;
  order: 1;
}
.flexbox .col:nth-child(2) {
  background: #eee;
  order: 0;
}
.flexbox .col:nth-child(3) {
  background: #eee;
  order: 2;
}

JavaScript 选项

当然,还有 JavaScript 等高列解决方案。我们在 “行中等高块” 中对此进行了一些探讨,并且 Michah Godbolt 提供了一个很好的 CodePen 演示,说明了此方法。

如果您想使用 jQuery 插件,请查看 matchHeight,它可以帮助您使选定元素的高度相等。

快速笔记

  • 使用百分比进行布局在 WebKit 中并不完美
  • 在这个演示中,我使用了诸如:nth-child 之类的方法来定位一些列。如果你为你的列指定特定的类名并使用这些类名,你可能会获得更好的跨浏览器兼容性。我更感兴趣的是在这里研究理论,而一些花哨的现代技术只在 :nth-child 可以工作的浏览器中有效。
  • 额外提示:如果你只有两列,可以使用类似于伪列的技术来实现流体宽度列。这在 CSS-Tricks 当前(v7)的设计中使用。布局是 2 列流体,侧边栏的背景颜色来自此图像。该列能够增长,因为该背景的放置使用百分比使其能够随着容器元素宽度的增长而正确调整。
    background: url(sidebar.png) repeat-y 61.7% 0;
  • 弹性布局模型与CSS3 布局模块非常不同。显然,就实际实现而言,风向正在转向弹性布局。