Beyond awesome | 越而胜己

盒模型是 CSS 中连我都有点了解的基础。在矩形元素内容的外围有三层东西:外边距 margin、边框 border、内边距 padding

今天的内容来自书中第一部分《基础回顾》的第三章《盒模型》,我们将深入了解盒模型的应用,实现一个两列布局的网页。

宽度问题

假设我们想做一个两列布局的网页,左边是 70% 宽的主栏 main,右边是 30% 宽的侧边栏。我们可以写出如下的 CSS:

body {
  background-color: #eee;
  font-family: Helvetica, Arial, sans-serif;
}

header {
  color: #fff;
  background-color: #0072b0;
  border-radius: .5em;
}

main {
  display: block;  // ie bug fix
}

.main {
  float: left;
  width: 70%;
  background-color: #fff;
  border-radius: .5em;
}

.sidebar {
  float: left;
  width: 30%;
  padding: 1.5em;
  background-color: #fff;
  border-radius: .5em;
}

但是这段 CSS 使得侧边栏跑到了第二行去,因为一个 70% 宽的元素和一个 30% 宽的元素加起来宽度却超过了 100%。

出现这样的错误的原因是 .sidebar 的内边距加大了它的宽度,使得总宽度超出了 3em

如何解决这样的问题?一种方法是减少 .sidebar 的宽度,比如改成 26%。但这种方式显然不解决问题,在某些情况下仍然可能翻车。另一种方式是用 calc 计算宽度,比如 calc(30% - 3em),但也并不优雅。

最好的解决方式是调整盒模型。在默认的盒模型 content-box 中,元素的高和宽指的是 margin 内圈的高和宽。因此,paddingborder 的宽度都将额外增加元素的尺寸。我们可以将 box-sizing 属性设为 border-box,此时元素的高和宽指的是 border 外圈的高和宽,就避免了计算 borderpadding 占用屏幕的空间。

给  .main.sidebar 都加上 box-sizing: border-box; 之后,问题得到了解决:

由于 border-boxcontent-box 有用的多,我们可以将默认值设为 border-box

*,
::before,
::after {
  box-sizing: border-box;
}

这种方式将把所有元素都改为 border-box。但是当我们使用一些使用 content-box 的第三方组建的时候,就无法将它们的 box-sizing 改回 content-box。一种解决方案是采用如下的代码:

:root {
  box-sizing: border-box;
}

*,
::before,
::after {
  box-sizing: inherit;
}

.third-party-component {
  box-sizing: content-box;
}

由于层叠可以覆盖继承,这样就可以保留某些元素的 content-box 了。

此时 .main.sidebar 还是紧贴在一起,我们可以用 margin-left: 1.5em; 将它们分开。但是由于 margin 是在宽度之外的,所以我们不得不用 calc() 来重新计算 .sidebar 的宽度:width: calc(30% - 1.5em);

这样我们就得到了想要的两列布局:

未完待续

下一篇笔记将会讨论盒模型中的高度问题。