CSS 盒模型(下)
盒模型的结构上一篇讲完了,这一篇处理它的两个疑难杂症,外边距合并和边距传递。它们都在垂直方向上捣乱,水平方向没有这些问题,这是记住它们的关键。
外边距合并
两个上下相邻的块级元素,上面的 margin-bottom 和下面的 margin-top 不会相加,而是取较大值,这就是外边距合并。
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }
直觉上两个元素间距该是 50 像素,实际只有 30 像素,多的那个赢了。这不是 bug,是规范设计的,目的是让段落之间的间距自然。但它确实常让新手困惑,调间距时总是”差一点”。
记住三个要点。第一,只发生在垂直方向,左右相邻的 margin 是正常相加的。第二,只发生在块级元素之间,Flex 和 Grid 容器里的项目不会合并,float 的元素也不会。第三,两个 margin 同正取大,一正一负取和,都是负数取更负的。
父子边距传递
更隐蔽的是边距传递,子元素的 margin-top 会跑到父元素外面去。
<div class="parent">
<div class="child">子元素</div>
</div>
.parent {
background: #ecf0f1;
}
.child {
margin-top: 40px;
}
预期的效果是,子元素往下挪 40 像素,父元素背景包住它。实际效果是,子元素没动,父元素整体往下挪了 40 像素,背景上沿空出一块。因为父元素没有内容隔开,浏览器的规则是,这种情况下子元素的 margin-top 和父元素的 margin-top 合并了,谁大谁生效。
破解办法
处理边距合并,思路就是打断合并的条件,让父子之间”有东西隔着”。常见四种。
/* 方法一,给父元素加 padding 或 border */
.parent {
padding-top: 1px;
}
/* 方法二,给父元素加 overflow: hidden */
.parent {
overflow: hidden;
}
/* 方法三,给父元素加 display: flow-root */
.parent {
display: flow-root;
}
/* 方法四,父元素改成 flex 或 grid 容器 */
.parent {
display: flex;
}
方法一是最朴素的理解,有了 padding 就有隔层,margin 就老实待在子元素身上。方法二和方法三原理相同,让父元素形成一个独立的块级格式化上下文,子元素再闹腾也出不了这个圈。方法四顺带把布局也升级了,现在的新项目里很常见。
实战习惯
与其每次踩坑再修,不如从源头减少。给元素只设单向的 margin,元素之间用 margin-bottom 或者 margin-top 单向控制,上下两个元素就不会因为双向 margin 合并而困惑。再配合全局 border-box 和现代布局,边距合并的出场率其实很低了。另外,调间距时心里多一步换算,设 20 加 30 不等于 50,合并后是 30,看浏览器开发者工具里的盒模型图,数值清清楚楚。
小结
垂直方向的两个坑,兄弟之间的外边距合并取大值,父子之间的边距传递会跑出父元素。解法归纳成一句话,让父子之间有隔层,或者把父元素变成新式容器。遇到间距诡异,先怀疑合并,再怀疑传递,打开开发者工具看一眼盒模型,答案就在那里。下一篇用盒模型的配件做点好玩的,圆角、阴影和透明度。