0%

可视化模型概述

作为css系列文章的第一篇,本文主要是通过阅读”css mastry”第三版整理而成,还希望大家指正。

在本篇文章中,我们着重学习理解CSS盒模型,外边距是如何以及为什么会发生合并的,不同的定位属性和值,浮动和清除浮动是如何工作的以及格式化上下文到底是个什么东西这五个问题。

盒模型回顾

我们知道,页面中的每一个元素都是一个由内容区,内边距,边框和外边距组成的矩形盒子。

内边距包围在内容区周边,比如我们为元素设定一个background属性,那么这个background就被应用到内容区和内边距区域。就其本身而言,内边距刚好形成了一个槽,就可以避免内容区与边框直接接触。

而边框就是在内边距外边形成的线,可以有点状,实线,虚线等多种样式。

在边框外面就是外边距,是盒子可视部分以外的透明区域,允许我们控制页面元素之间的距离。

而另一个不影响布局的属性是outline属性,这个属性在元素的边框外面画一条线,不影响盒子的宽高,在debug一些复杂的布局和证明一个布局效应的时候会比较有用。

内边距,边框和外边距默认是为零的,但是一些用户代理样式表会为许多元素设定内边距和外边距,比如,标题元素总是默认存在一些外边距,并且这些随着浏览器的不同而变得不同,我们可以在自己定义的样式表中重写这些属性或者使用引入的css reset样式表。


box-sizing

在默认情况下,元素的宽高属性指的是内容盒的宽度和高度,为元素添加内边距和边框不会影响内容盒的大小而是会增加元素盒子的整体大小。如果你想要一个5px内边距,5px边框在每一边,并且总的宽度为100px,就需要设置元素的width属性为80px。此外,如果盒子还有个10px的外边距,那么它就会占据总共120px的空间。

我们可以通过box-sizing这个属性去改变盒子模型宽度的计算方式,默认的box-sizing值为content-box,而其表现正如我们上面所述。但是,有另外的方式去影响宽高在响应式布局中是非常有用的。

我们可以设置box-sizing的属性为border-box,这样一来,宽高的计算方式就包含进了内边距和边框以及内容盒子的宽高,但依然不包括外边距,而外边距依然影响着元素占据的总空间。对于上面的例子而言,我们就可以设置box-sizing的值为boeder-box,这时就不用计算得到内容区的宽度了,而只需要设置宽度为100px就好了,其他的属性和值保持依旧,就实现了和上面一样的效果了。也可以理解为border-box之后,设定的width属性就是盒子的可视区域宽度。

那么为什么这样是有用的呢?在许多情况下,这种处理宽高的方式都是更符合人的直觉的,并且这也是在IE6之前的IE浏览器实际的工作方式。

内边距,边框和外边距可以被应用到盒子的一边或者多边,外边距也能够被赋予一个负的值,这在一些拉入和抽出元素本身在页面的位置的场景下是比较有用的,后面的文章里会讨论这些技术。

你可以用任何长度单位比如px,ems和百分比去度量在CSS声明中去为元素添加内边距和外边距。使用百分比外边距的时候会有一些奇怪的行为确实值得提一下,假设上面的例子,我们设置左侧外边距为5%会发生什么呢?

答案就是左侧外边距为父元素5%的宽度,比如父元素的宽度为100px,那么它的左
侧外边距就为5px。

当设置上下外边距为百分比形式的时候,你可能会认为,这个百分比来自于父元素的高度。这看来似乎是符合逻辑的,然而,因为高度通常是不显式声明的,而且可能因为随着内容区高度的变化变得失控,所以CSS规范中也说明上下外边距和内边距的百分比来自于它的包含块的宽度。在这个例子中,包含块就是父级元素,但是包含块是可以变化的。我们将会在后面的文章里面阐述这究竟意味着什么。


最小和最大值

有时候为元素应用最小宽度和最大宽度在响应式布局中可能是非常有用的,因为它允许块级元素默认自动填充满父级元素的宽度,但是不会缩减到更窄以至于小于最小宽度指定的值,或者是拉伸到更宽以至大于最大宽度指定的值。

相似地,最小高度和最大高度也同样存在,但是当在CSS中应用任何高度值你应该特别小心。因为元素的高度最好是被它包含的内容所隐式指定。否则,如果内容增加的时候,或者文本大小改变了,内容将会溢出固定高度的盒子。如果你因为某种原因确实要设置默认的高度,用min-height属性是更好的,因为它能够让你的盒子随着内容扩展而增大。


视觉格式化模型

随着对盒模型的理解,我们能够开始探索一些视觉格式化和定位模型。

人们经常把p元素,h1元素和article元素叫做块级元素。这意味着他们视觉上是作为内容块或者块级盒子(block boxes)显示的。相反地,像strong,span和time元素被描述成内联级元素,因为他们的元素作为内联盒子(inline boxes)在行(lines)内显示。

通过元素的display属性可以改变生成盒子的类型。这意味着你可以通过设置像span一样的内联元素的display属性为block使得它可以表现的像一个块级盒子一样。也可以通过设置display属性为none使得元素根本不生成任何盒子,如此一来,这个盒子就不再显示并且在文档中占据任何空间了。

在CSS中有许多不同的定位模型,包括浮动,绝对定位和相对定位。除非特别指定,所有的盒子生下来就是默认定位在normal flow中并且默认值为static。如同名字表达的意思一样,在normal flow中的盒子的位置将会按照HTML文档的顺序被安排。

块级盒子将会一个接一个的出现在垂直位置上。盒子间的垂直距离将会被盒子的垂直外边距计算。

内联盒子在一行上被水平布局,遵循文本流并且在换行时换行到新行。他们的水平空间可以通过水平内边距边框和外边距调整,然而,垂直内边距边框和外边距将不会对一个inline box产生影响。类似地,指定一个明确的宽度和高度也是没有任何作用的。

一行文本形成的水平方向的盒子被叫做一个line-box,并且一个line-box总是足够高到容纳他所有的inline-box。唯一改变line-box尺寸的方式就是修改行高或者在他内部的inline-box的水平内边距边框和外边距。

你也可以设置元素的display属性为inline-block,如同名字表达的那样,这个声明使得元素水平对齐就好像inline-box那样。然而,盒子的内部表现的就像盒子是块级一样,看可以被精确地指定宽度高度垂直内边距和外边距等。

当你用表格标记(比如table,tr,th,td元素等)的时候,table本身表现为一个块级元素,然而表格的内容将会根据生成的行和列对齐(line up)。也可以去为其他元素设置display属性使得他们产生像表格布局一样的行为。通过用正确的方式去应用table,table-row和table-cell的值,你可以不需要用table标记就实现一些HTML表格元素的特性。

flexbox和Grid Layout大大扩展了display属性,这些我们在后面文章中讨论。通常来说,这些新的布局模式创造的盒子在外部上下文中表现的就像块级盒子一样,但是在内部对于元素是如何显示的创建了一套新的规则。

这种外部和内部显示模式的分离(比如inline-block,flexbox,grid)正在Display Level 3模块中被标准化。在那里,已存在关于显示模式的特性和关键词正在被扩展以允许更加细粒度的控制。重要的结论是内联级元素和块级元素仍然是html元素默认行为的基本,但事实有略微的差别。


匿名盒子(Anonymous Boxes)

如同HTML可以嵌套一样,盒子也能够包含其他的盒子。大多数的盒子是被准确定义的元素形成的。然而有一种情况是块级盒子会被创建即便它没有被明确的定义——当我们在块级元素例如section元素的开始处添加一些文本,如同下面那样,即便没有定义“some text”作为块级元素,它还是被当作块级元素对待的。

1
2
3
4
<section>
some text
<p>Some more text</p>
</section>

在这种情况下,这个盒子被描述为一个匿名的块级盒子(anonymous block box),因为它没有被关联为一个明确定义的元素。

相似的事情发生在块级元素内部的文本组成的行盒(line boxes)。假定有一个段落包含三行文本,那么每一行文本都形成一个匿名的行盒(anonymous line box),不能够直接对匿名块级盒子或者行盒直接定义样式,除非通过:first-line伪元素,已经被限制使用并且只允许你改变字体和颜色。然而,理解在屏幕上看到的所有元素创建某种形式的盒子是特别有用的。


外边距合并(Margin Collapsing)

当谈到常规的块级盒子时,有一种叫做外边距合并的行为。外边距合并是一个相对简单的概念。然而在实践中,当你布局一个web页面的时候,他可能会导致一些困惑。简单来说,当两个或者更多垂直外边距相遇的时候,他们会合并形成一个外边距。这个外边距就等于二者当中比较大的那个外边距的高度。

当两个元素在一起时,第一个元素的底部外边距将会合并第二个元素的顶部外边距。

当一个元素包含在另一个元素内部的时候,假定没有内边距和边框分隔外边距,他们的顶部和底部外边距也会被合并。

起初看来这可能很奇怪,但是外边距居然还会和自身合并。假定有一个空元素,只有外边距没有内边距和边框,在这种情况下,顶部的外边距触摸到底部的外边距,那么就会合并在一起。

如果上面的外边距触摸到其他元素的外边距,将会合并他自己。

这就是为什么许多空的段落元素占据非常小的空间的原因,因为他们的外边距合并成为一个单一的小的外边距了。

外边距合并看上去很奇怪,但事实上是很有道理的。拿一个几个段落的组成的文本页,如果没有外边距合并,第一段和第二段之间的间距就是二倍的第一段的上外边距。正因为有了外边距合并,在每一段之间的边距合并,使得这个间距和其他地方的保持一致。

外边距合并只发生在normal flow中块级盒子的垂直外边距。像inline-box,浮动盒子或者是绝对定位盒子不会发生外边距合并。


包含块(Containing block)

给一个元素包含块的概念是十分重要的,因为它决定了像之前看到的百分比padding和margin这些属性是如何解释的。

元素的包含块取决于元素是如何定位的。如果元素是static定位的话(不声明position属性或者显式设置position)或者相对定位,他的包含快被计算到最近的父元素的边缘,这个父元素的display属性会产生一个类块的上下文,包括block,inline-block,table-cell,list-item等等。

默认地,宽高外边距和内边距设定成百分比的时候都是基于父元素的尺寸计算的。当你指定一个元素是绝对定位或者fix定位的时候就会有所改变。下面我们将学习不同的定位模型以及他们是如何和包含块交互的。


相对定位

当你为一个元素指定相对定位的时候,初始情况下元素仍然在原来所在的位置,只有当设置了上下左右属性值的时候元素才会发生相应的偏移,并且元素仍然占据原本的空间,不脱离文档流,只是相对原来的位置做相对偏移。偏移之后,元素可能会覆盖其他元素。


绝对定位

相对定位被认为是normal-flow定位模型的一部分,因为元素是相对于它在文档流中的位置进行定位的。相反地,绝对定位使得元素脱离文档流,因此不占据任何空间。其他在文档流中的元素表现地就好像绝对定位元素不在那里一样。


2017 年 12月 31日