高级的布局话题基于文档流和盒模型等概念,这些是决定网页元素的大小和位置的基本规则。
一、元素宽度问题
IE有一个bug,它会默认将<main>元素渲染成行内元素,而不是块级元素,所以代码中我们用声明display: block来纠正。
当给一个元素设置宽或高的时候,指定的是内容的宽或高,所有内边距、边框、外边距都是追加到该宽度上的。
1.1 避免魔术数值
在编程中不推荐魔术数值,因为往往难以解释一个魔术数值生效的原因。如果不理解这个数值是怎么来的,就不会知道在不同的情况下会产生什么样的结果。
1.2 调整盒模型
box-sizing的默认值为content-box,这意味任何指定的宽或高都只会设置内容盒子的大小。将box-sizing设置为border-box后,height和width属性会设置内容、内边距以及边框的大小总和,这刚好符合示例的要求。
盒模型:CSS盒模型有两种类型:标准盒模型和IE盒模型。标准盒模型中,元素的宽度和高度只包括内容区域,不包括内边距、边框和外边距。而在IE盒模型中,元素的宽度和高度包括内容区域、内边距和边框,但不包括外边距。
1.3 全局设置border-box
用通用选择器(*)选中了页面上所有元素,并用两个选择器选中了网页的所有伪元素。并将这段代码放到样式表开头。
加上这段代码,height和width会指定元素的实际宽和高。改变边框和内边距不会影响它们。
但是全局设置border-box为固定值可能会在使用第三方组件时出现适配问题,而使用继承方式来修改盒模型则更为灵活,可以通过修改顶级容器中的border-box值来更改。
因此,可利用继承来修改盒模型。
盒模型通常不会被继承,但是使用inherit关键字可以强制继承。建议将代码添加到新网站的开发中,但是注意已有的样式表代码不可随意添加。
1.4 给列之间加上间隔
百分比是相对于父元素的完整宽度的。
如果想用其他单位指定间距呢?(用em指定间距,因为em单位的一致性更好。)可以用calc()来实现。
二、元素高度的问题
为什么将高度区别于宽度单提出来学习?因为两者处理方式不同,宽度是有限制的,而通常是避免给高度明确限制的。
普通文档流是为限定的宽度和无限的高度设计的。容器的高度由内容天然地决定,而不是容器自己决定。
普通文档流——指的是网页元素的默认布局行为。行内元素跟随文字的方向从左到右排列,当到达容器边缘时会换行。块级元素会占据完整的一行,前后都有换行。
2.1 控制溢出行为
当明确设置一个元素的高度时,内容可能会溢出容器。
用overflow可以控制溢出的行为:
- visible(默认值)——所有内容可见,即使溢出容器边缘。
- hidden——溢出容器内边距边缘的内容被裁剪,无法看见。
- scroll——容器出现滚动条,用户可以通过滚动查看剩余内容。
- auto——只有内容溢出时容器才会出现滚动条。(推荐使用)
请谨慎使用滚动条,因为网页外部存在滚动条,网页内容设置滚动条影响用户体验。
除了垂直溢出,内容也可能在水平方向溢出。可以用overflow-x属性单独控制水平方向的溢出,或者用overflow-y控制垂直方向溢出。
2.2 百分比高度的备选方案
- 要想让百分比高度生效,必须给父元素明确定义一个高度。
- 更好的方式是用视口的相对单位vh,100vh等于视口高度。
- 更常见方法——创造等高列。
1. 等高列
这里等高列的原意是不明确设置容器元素的高度,容器元素的高度随着子元素列的内容而改变,通常就是具有更高的列作为容器元素的高度,而内容较少的列会留白,拉到与最高列的高度,同时保持底部对齐。
以下是通过CSS表格和Flexbox两种方式实现等高列效果。
2. CSS表格布局
首先,用CSS表格布局替代浮动布局。给容器设置display: table,给每一列设置display:table-cell。
不像block的元素,默认情况下,显示为table的元素宽度不会扩展到100%,因此需要明确指定宽度❶。外边距❷并不会作用于table-cell元素,导致缺少间隔。
可以用表格元素的border-spacing属性来定义单元格的间距,设置水平间距和垂直间距。但是注意这个值会作用于表格的外边缘。
可以用负外边距解决问题。正的外边距会将容器的边缘往里推,而负的外边距则会将边缘往外拉。
3. Flexbox
Flexbox不需要一个额外的div包裹元素,它默认会产生等高的元素。此外也不需要使用负外边距。给容器设置display: flex,它就变成了一个弹性容器(flex container),子元素默认等高。
给子元素设置宽度和外边距,尽管加起来可能超过100%, Flexbox也能妥善处理,不用calc()计算。
注意:除非别无选择,否则不要明确设置元素的高度。先寻找一个替代方案。设置高度一定会导致更复杂的情况。
2.3 使用min-height和max-height
min-height和max-height:可以用这两个属性指定最小或最大值,而不是明确定义高度,这样元素就可以在这些界限内自动决定高度。
这意味着元素至少等于指定的高度,如果内容太多,浏览器就会允许元素自己扩展高度,以免内容溢出。
同理,max-height允许元素自然地增高到一个特定界限。如果到达这个界限,元素就不再增高,内容会溢出。还有类似的属性是min-width和max-width,用于限制元素的宽度。
2.4 垂直居中内容
过去有好几种方式实现垂直居中,但是每一种方式都有一定的局限性。在CSS中,回答一个问题的答案通常是“这得看情况”,垂直居中就是如此。
vertical-align声明:只会影响行内元素或者table-cell元素。
- 对于行内元素,它控制着该元素跟同一行内其他元素之间的对齐关系。
- 对于显示为table-cell的元素,vertical-align控制了内容在单元格内的对齐。如果你的页面用了CSS表格布局,那么可以用vertical-align来实现垂直居中。
在容器里让内容居中最好的方式是根据特定场景考虑不同因素。
1. 可以用一个自然高度的容器吗?
给容器加上相等的上下内边距让内容居中。
2. 容器需要指定高度或者避免使用内边距吗?对容器使用display: table-cell和vertical-align: middle。
3. 可以用Flexbox吗? 如果不需要支持IE9,可以用Flexbox+align-items: center;+justify-content: center; 居中内容。
4. 容器里面的内容只有一行文字吗?
设置一个大的行高,让它等于理想的容器高度。这样会让容器高度扩展到能够容纳行高line-hight。如果内容不是行内元素,可以设置为inline-block。
5. 容器和内容的高度都知道吗?
将内容绝对定位。(只有当前面提到的方法都无效时才推荐这种方式。)父元素relative相对定位,子元素absolute绝对定位;
6. 不知道内部元素的高度?
用绝对定位结合变形(transform)。(还是只有当前面提到的方法都无效时才推荐该方法。)
7. 还不确定的话?
参考howtocenterincss网站。这个网站很不错,可以根据自己的场景填写几个选项,然后它会相应地生成垂直居中的代码。
三、负外边距
外边距不同于内边距和边框宽度,可以为负值。
负外边距的具体行为取决于设置在元素的哪边。
- 如果设置左边或顶部的负外边距,元素就会相应地向左或向上移动,导致元素与它前面的元素重叠;
- 如果设置右边或者底部的负外边距,并不会移动元素,而是将它后面的元素拉过来(元素指定宽度或者长度时);元素指定宽度时设置右边的负外边距会将元素右边拓展到容器外。
负外边距都使文档流向前流动,但是不会改变元素本身在文档流中所占的内容大小。
如果元素被别的元素遮挡,利用负外边距让元素重叠的做法可能导致元素不可点击。
四、外边距折叠
当顶部和/或底部的外边距相邻时,就会重叠,产生单个外边距。这种现象被称作折叠。
4.1 文字折叠
外边距折叠的主要原因与包含文字的块之间的间隔相关。折叠外边距的大小等于相邻外边距中的最大值。
4.2 多个外边距折叠
即使两个元素不是相邻的兄弟节点也会产生外边距折叠。在没有其他CSS的影响下,所有相邻的顶部和底部外边距都会折叠。如果在页面中添加一个空的、无样式的div(没有高度、边框和内边距),它自己的顶部和底部外边距就会折叠。
注意:只有上下外边距会产生折叠,左右外边距不会折叠。
4.3 容器外部折叠
如果元素的容器有背景色,元素的外边距在容器外面折叠通常会产生不想要的效果。外边距会在不想的地方重叠。
防止外边距重叠的方法:
- 对容器使用overflow: auto(或者非visible的值),防止内部元素的外边距跟容器外部的外边距折叠。这种方式副作用最小。———创建了BFC
- 在两个外边距之间加上边框或者内边距,防止它们折叠。
- 如果容器为浮动元素、内联块、绝对定位或固定定位时,外边距不会在它外面折叠。
- 当使用Flexbox布局时,弹性布局内的元素之间不会发生外边距折叠。网格布局同理。
- 当元素显示为table-cell时不具备外边距属性,因此它们不会折叠。此外还有table-row和大部分其他表格显示类型,但不包括table、table-inline、table-caption。
BFC的特点: 可避免盒子塌陷和外边距重叠BFC内部布局不影响外部布局。
创造BFC的条件: 元素设置绝对定位:position (absolute、fixed);
display 值为:inline-block、table-cell、table-caption、flex、float等;
overflow 值为:hidden、auto、scroll;
五、容器内的元素间距
容器的内边距和内容的外边距之间的相互作用处理起来很棘手。
5.1 如果内容改变了
每一次改变HTML,得确保每个元素之间有间距,但是容器的顶部(或底部)没有多余的间距。
5.2 更通用的解决方案:猫头鹰选择器
不要给网页当前的内容固定外边距,而是应该采取更通用的方式,不管网页结构如何变化都能够生效。
这就是Heydon Pickering所说的迟钝的猫头鹰选择器(lobotomized owl selector)(以下简称猫头鹰选择器),因为它长这样:* + *。该选择器开头是一个通用选择器(*),它可以选中所有元素,后面是一个相邻兄弟组合器(+),最后是另一个通用选择器。也就是说,它会选中页面上有着相同父级的非第一个子元素。
使用猫头鹰选择器是需要权衡的。它省去了许多的需要设置外边距的地方,但是在某些不想加外边距的地方则需要覆盖。通常只在有并列元素,或者有多列布局时这样使用。有时还需要根据设计,给段落和标题设置特定的外边距。
六、总结
- 总是全局设置border-box,以便得到预期的元素大小。
- 避免明确设置元素的高度,以免出现溢出问题。
- 使用现代的布局技术,比如display: table或者Flexbox实现列等高或者垂直居中内容。
- 如果外边距的行为很奇怪,就采取措施防止外边距折叠。
- 使用猫头鹰选择器全局设置堆叠元素之间的外边距。