2

一直对盒模型的了解很零碎,学习都来源于各个文章的只言片语,这样学来的既不完善,也不持久。最近通读了w3的官方文档,对其中的内容进行一点归纳吧。

盒子的概念

盒子模型指的是浏览器将文档流从视觉上渲染为不同矩形框的组合。
浏览器将文档流中的每个元素都渲染为一个矩形框,其包含内容区域(如文本、图片)和可选的margin/border/padding三个区域,中文名为外边距、边框和填充。如下图所示:
图片描述

这四个区域每一个都可以划分为上下左右4个部分的边界,如上边距、下边距,上边框、下边框等。

内容边界:包裹着元素宽度和高度确定的盒子的四个线段组成。
填充边界:包裹着元素填充区域的四个线段组成,如果填充宽度为0,则等于内容边界。
边框边界:包裹着元素边框区域的四个线段组成,如果边框宽度为0,则等于填充边界。
外边距边界:包裹着元素外边距区域的四个线段组成,如果外边距宽度为0,则等于外边距边界。

其中内容区域的盒子的宽度和高度由几个因素来进行确定,是否设置了宽度和高度值,是否包含文本内容或者其他的盒子,盒子类型是否为table相关等等。
内容和填充区域的颜色由background属性来设置,边框区域的颜色由border-color || color其中一个属性来设置,外边距区域的颜色为transparent透明色。

实例

下面的插图展示了margin/border/padding如何被渲染(图二是更为详细的切分):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
  <HEAD>
    <TITLE>Examples of margins, padding, and borders</TITLE>
    <STYLE type="text/css">
      UL { 
        background: yellow; 
        margin: 12px 12px 12px 12px;
        padding: 3px 3px 3px 3px;
                                     /* No borders set */
      }
      LI { 
        color: white;                /* text color is white */ 
        background: blue;            /* Content, padding will be blue */
        margin: 12px 12px 12px 12px;
        padding: 12px 0px 12px 12px; /* Note 0px padding right */
        list-style: none             /* no glyphs before a list item */
                                     /* No borders set */
      }
      LI.withborder {
        border-style: dashed;
        border-width: medium;        /* sets border width on all sides */
        border-color: lime;
      }
    </STYLE>
  </HEAD>
  <BODY>
    <UL>
      <LI>First element of list
      <LI class="withborder">Second element of list is
           a bit longer to illustrate wrapping.
    </UL>
  </BODY>
</HTML>

图片描述

需要注意的是:

  1. 内容区域的计算是自上而下的,所有的li元素的内容区域计算都基于ul父元素。
  2. margin盒子包含了content/padding/border/margin四部分的总和,在纵向上会出现margin的合并。
  3. margin的颜色总是透明的,所以颜色会是父元素的透射颜色。

margin属性

margin属性包括margin-top/margin-right/margin-bottom/margin-left 和 margin

margin-top/margin-right/margin-bottom/margin-left
值:<margin-width> || inherit || auto
初始值: 0
适用于:除了table展示类型的所有元素,table类型里依然适用的有`table-caption/table/inline-table`
继承: 否
百分比:根据父包裹容器的宽度
计算值:指定的百分比或固定值

需要注意:margin-top/margin-bottom属性对行间元素不起作用。
如果使用auto,同时父元素具有对应的宽度和高度,将在计算时均分空余空间达到居中的效果。

margin是以上四种属性的简写,如果只有一个值,则设置所有边界;两个值则第一个值为top/bottom值,第二个值为left/right值;三个值则第一个值为top值,第二个值为left/right值,第三个为bottom值;四个值则分别对应top/right/bottom/left四个边界。

body { margin: 2em }         /* all margins set to 2em */
body { margin: 1em 2em }     /* top & bottom = 1em, right & left = 2em */
body { margin: 1em 2em 3em } /* top=1em, right=2em, bottom=3em, left=2em */

margin的边界合并

在CSS里,两个相邻元素(并不一定是兄弟元素)的外边距将会发生合并为一个外边距,这个过程我们成为collapse。这个过程只会发生在垂直方向上的margin,合并结果是所有外边距的最大值。
边界合并的设置是为了元素布局的合理展示,如段落间的距离不会成为顶部的两倍。
边界的合并适用于margin-top/margin-bottom,不仅仅包括兄弟元素的合并,还有父子元素甚至自身的上下边距合并。
注意:只有普通文档流中块框的垂直外边距才会发生外边距合并。行内框、浮动框或绝对定位之间的外边距不会合并。
下图展示margin合并的效果图(来源于w3school文档):
兄弟元素外边距合并:
图片描述

父子元素外边距合并:
图片描述

自身元素外边距合并:
图片描述

另外官方文档里关于合并还有很多的特殊情况,不再一一说明,粘贴源文档:

Adjoining vertical margins collapse, except:

Margins of the root element's box do not collapse.
If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.
Horizontal margins never collapse.

Two margins are adjoining if and only if:

both belong to in-flow block-level boxes that participate in the same block formatting context

no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
 - top margin of a box and top margin of its first in-flow child
 - bottom margin of box and top margin of its next in-flow following sibling
 - bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
 - top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', zero or 'auto' computed 'height', and no in-flow children

A collapsed margin is considered adjoining to another margin if any of its component margins is adjoining to that margin.

Note. Adjoining margins can be generated by elements that are not related as siblings or ancestors.

Note the above rules imply that:

 - Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
 - Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children.
 - Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
 - Margins of inline-block boxes do not collapse (not even with their in-flow children).
 - The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, unless that sibling has clearance.
 - The top margin of an in-flow block element collapses with its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.
 - The bottom margin of an in-flow block box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.
 - A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.

When two or more margins collapse, the resulting margin width is the maximum of the collapsing margins' widths. In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.

If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.

If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
Otherwise, either the element's parent is not taking part in the margin collapsing, or only the parent's bottom margin is involved. The position of the element's top border edge is the same as it would have been if the element had a non-zero bottom border.
Note that the positions of elements that have been collapsed through have no effect on the positions of the other elements with whose margins they are being collapsed; the top border edge position is only required for laying out descendants of these elements.

顺便讲一个常见的bug:垂直外边距合并问题常见于第一个子元素的margin-top会顶开父元素与父元素相邻元素的间距。其原因为父元素和第一个子元素的外边距进行合并,并选取其最大值,即第一个元素的上边距,导致撑开父元素上边距。解决方案为:设置父元素的border或padding将子元素控制在父元素content内部,防止发生margin合并,即给外部div设置一个透明的1像素的border-top即可。

.outer-box{
  border-top:1px solid transparent;
}

Padding属性

padding属性包括padding-top/padding-right/padding-bottom/padding-left 和 padding

padding-top/padding-right/padding-bottom/padding-left
值:<padding-width> || inherit
初始值: 0
适用于:除了`table-row-group/table-header/group/table-footer-group/table-row/table-column-group/table-column`的所有元素
继承: 否
百分比:根据父包裹容器的宽度
计算值:指定的百分比或固定值

需要注意:margin-top/margin-bottom属性对行间元素不起作用,且值不能为负。

margin是以上四种属性的简写,设置的内容和上面一致,元素的个数和含义与margin相同。

border属性

border属性包括border-width/border-color/border-style,而其中每一个都可以切分为4块,如border-left-width/border-top-color。对于不同的用户代理(浏览器)会对部分的元素渲染出特定的样式来,如button,menus等。

border-top-width等
值:<border-width> || inherit || thin || medium || thick
初始值: medium
适用于:所有元素
继承: 否
百分比:N/A,无法设置
计算值:绝对值,如果为none/hidden则转换为0

需要注意:thin/medium/thick由不同的浏览器实现,具体的值不尽相同。

border-top-color等
值:<color> || transparent || inherit
初始值: color的值(字体颜色)
适用于:所有元素
继承: 否
百分比:N/A,无法设置
计算值:指定的值或者字体颜色color的值
border-top-style等
值:none || hidden || dotted || dashed || solid || double || groove || ridge || inset || outset || inherit
初始值: none
适用于:所有元素
继承: 否
百分比:N/A,无法设置
计算值:指定的值

需要注意:边框会绘制在所有盒子背景色的上方,在groove/ridge/inset/outset值时,具体的颜色可能会被浏览器强制修改,而不仅限于border-color属性。初始值为none,所以一定要进行设置样式,否则看不到。

border属性:    [ <border-width> || <border-style> || <'border-top-color'> ] || inherit

需要注意:border属性不能像其中三个子属性一样分别设置,如果设置其中一个值,则会应用到所有四条边界上。

margin是以上四种属性的简写,设置的内容和上面一致,元素的个数和含义与margin相同。

下面的两段样式只是出现的顺序不同会显示出不同的效果来,原因是设置的顺序影响到值的覆盖:

// 左边框为双细线和黑色
blockquote {
  border: solid red;
  border-left: double;
  color: black;
}
// 边框全部为实线和红色
blockquote {
  border-left: double;
  color: black;
  border: solid red;
}

双向上下文的行间元素的盒模型

在每一个行间元素中,浏览器需要产生每一个盒子按照视觉顺序而非逻辑顺序(也就是direction设置的方向)进行渲染。
下面的两句话我实在是看不懂在讲什么,贴出原文:

When the element's 'direction' property is 'ltr', the left-most generated box of the first line box in which the element appears has the left margin, left border and left padding, and the right-most generated box of the last line box in which the element appears has the right padding, right border and right margin.

When the element's 'direction' property is 'rtl', the right-most generated box of the first line box in which the element appears has the right padding, right border and right margin, and the left-most generated box of the last line box in which the element appears has the left margin, left border and left padding.

盒模型相关的周边问题

  1. inline-block元素之间的小空白
 <STYLE type="text/css">
   div {background: #d3cfad;}
   label {background: lightblue;}
 </STYLE>

 <BODY>
  <div>
    <span>第一个元素</span>
    <!-- 这里的换行和制表符将被转化为一个空格 -->
    <span>第二个元素</span>
  </div>
 </BODY>

效果入图:
图片描述
原因为:HTML 中的换行符、空格符、制表符等合并为空白符, 字体大小不为 0 的情况下, 空白符自然占据一定的宽度, 因此产生了元素间的空隙.
解决方案:可以为inline-block元素设置为负的margin-left; 或父元素font-size设置为0, 子元素再重新设置回来等方法。
推荐方案:直接在HTML文档里改变文本排列,使行间元素尾标签和下一个头便签间不留任何空格,如

<span>第一个元素</span
><span>第二个元素</span>

因为这样影响范围最小,不需任何额外代码。

参考文献

  1. w3c官网文档 - 盒子模型:https://www.w3.org/TR/CSS2/bo...
  2. 你不知道的margin属性:http://louiszhai.github.io/20...元素之间空白Bug
  3. w3school - CSS外边距合并:http://www.w3school.com.cn/cs...

赵帅强
3.3k 声望380 粉丝

前端打工人