前端--关于CSS盒模型

时间:2022-11-11 09:38:38

CSS样式规则的学习是很繁琐和枯燥的,因为它不像物理、数学或者其他编程语言一样有一些基本概念、有一些基本公理或者规则,其他所有的表现都是概念在这些公里或者规则之下的逻辑游戏,CSS是有一些基本概念,但它没有说给你几条规则然后所有的表现都是在这些规则之下的任意*组合,你要推测一个样式声明块的在页面中具体是怎样表现的,你几乎靠非常少是靠逻辑推理去推测出来,绝大多数是你知道并记住了这种写法的声名块是具有怎样的表现的。之所以为这样也是因为样式的组合太繁琐组合情况特别多,像同样的概念margin在块级中的表现和在行内元素中的表现是不一样的,即便是同样在块级元素中不同方向上的margin表现也是不一样的,再进一步就算是同样在块级元素中同样的方向上,父元素的某些属性不同其表现也是不一样的,再加上浮动定位又是另一种情况,要是再考虑到各种不同浏览器的兼容性的话那就更令人烦躁了,这样的特性是不可能存在一个或几个规则让你逻辑推理样式表现的,掌握CSS样式规则只有穷举各种各样的组合并且记住它们的表现(虽然可能会有一小点的逻辑推理),因此前端这个职业感觉工作经验比其他的编程语言更加重要一些。

掌握一门语言的结果是在头脑中可以形成一个清晰的知识系统脉络图,而CSS的学习很难有一个清晰的逻辑线路去循环渐进的学习,因为它的概念关联性太弱了,所以在没有很清晰的逻辑帮助理解概念时,很好的理解现有概念是非常有必要的,比如说盒模型。

盒模型以及相关知识点在CSS中占有非常重要而核心的地位,而大家常常听到的非常经典的对盒模型的理解就是一个盒子的类比,盒子本身是border,里面货物就是内容,填充就是padding,盒子在摆放的时候要分开一定的距离就是margin。这种类比有它的好处,但是仍然感觉像是和没说一样(至少我这么觉得)。而且这种类比无形中把padding和内容这个整体视为标签元素的内容,把margin视为内容以外的东西,感觉这对理解css布局的相关本质会造成一定的偏差,最明显的一个例子就是初学人员会完全没有意识到width和height指定的宽和高是内容的宽和高(css3加了新的属性可以更改宽高的指定范围),并不是把padding和border还有margin算在一起的。所以现在我并不想把内容、padding、border、margin放到一个盒子相关中去理解,以一个不是盒模型的角度去理解也同样可能(只是可能因人而异)有利于避开在盒模型类比下形成想当然的认识。


在起初接触前端的时候都会看到这样的话:html标记页面内容,css表现页面样式,javascript控制页面行为。这是说页面的内容都是用html标签标记出来的,一个内容一定有一个相关html标签去标记它;css做到的是表现与内容分离,也就是说css是为页面的内容添加各种各样的样式,它的操作目标是内容,所以关于CSS的概念以内容为起点讨论,而不以盒子为起点。

打开网页可以发现页面的内容范围是文字、表单相关(输入框、按钮、单选框、复选框、下拉框)、图片、音频、视频。内容在页面中会放到一个矩形区域中即内容区,控制该区域的宽高属性为width和height,在内容区外部分则是标签元素形成的用于布局用的额外的空间。
标记这些内容的html标签分为两种:替换元素与非替换元素。
替换元素:以上除了文字都是替换元素,替换元素是指这个标签元素为某个内容的占一个位子,在原始html代码中看不出来实际具体表现。像图像、音频、视频标签它们就是指向某个图片文件、音频文件、视频文件。起占位作用的标签就是一种替换元素。
非替换元素:标签元素标记的内容在原始html代码中可以看出来,就不如说用span标签标记的文本。标记文本的span标签就是一种非替换元素。

因为内容总是有一个标签来标记它,而标签在浏览器中的渲染结果就是在内容周围变成一个矩形区域(在CSS3中可以设置为非矩形区域),这种矩形区域会在内容周围生成额外的空间,而产生的额外空间的种类一共有三种:padding(内边距)、border(边框)、margin(外边距)。在正常文档流中css对内容的样式布局实现大部分就是通过设置padding、border、margin来实现的。

padding空间是在边框和内容区域之间的无色透明的一块区域,该区域可以看到元素的背景色并且在没有border和margin的情况下该区域是不能合并的。
border空间是在内边距和外边距之间的一块有内置样式有颜色的一块区域,该区域也是绘制在背景色之上的,因此如果边框有镂空是可以看到背景颜色的。
margin空间是在边框外面的一块无色透明区域,该区域可以看到父元素的背景色并且在正常流中垂直方向上该区域是可以合并的,水平方向上是不存在合并规则的。

以上三个空间区域在在默认情况下都是不存在的。

虽然每个元素都会在内容周围渲染成矩形区域,但不同元素默认渲染成的矩形区域类型可能是不同的。通过设置display可以在不同类型的矩形区域之间进行切换。对矩形区域分类的一个标准就是看矩形区域前后是否有换行符:前后都有换行符的矩形区域为块级矩形区域;前后没有换行符的矩形区域为行内矩形区域。相应的不同类别的矩形区域所对应的元素也就叫做块级元素或者行内元素。换行符只是块级和行内与否的一条区分标识,并不是区别的全部,他俩在默认状态和变化行为上也有些许差别。
在文档流中默认状态下,多个块级元素是垂直排列的,并且它的宽为父元素的内容区域宽,高为自己内容区域高,可以*改变自己内容区的宽和高。而多个行内元素是横向排列的,它的宽和高都为内容区域的宽和高,并且不能设置内容区的宽和高。


这样,一块标签标记的内容在页面中的表现从里到外就是内容区、内边距、边框、外边距四部分,这四部分是紧密相连的,并作为一个整体放在一个布局上下文中,并基于这个布局上下文摆放。布局上下文是父元素的内容区,这个内容区可能在块级元素中也可能在行内元素中。一般情况下,各个标签内容能按照从左到右的顺序摆放的就按照从左到右的顺序摆放,从左到右摆放不下的(像超出或者有换行符)就按照从上到下的顺序摆放,如果一个元素标签处在这样一个摆放顺序中,就说这个标签在文档流中。只有浮动和定位会摆脱这种束缚,这时就说元素脱离了文档流。

 

矩形区域中各个属性的取值只有margin可以取负值,只有width和margin可以取auto关键字,border不能为百分数,其他属性都只能取 正的数字或者正的百分数。其中margin和padding设置的值是一个块区域的垂直高度,这块区域的长和宽是随着内容的width和height变化 而变化的。border设置的是自身的宽度,其长度也是随着相应边的长度变化而变化的。padding和border的取值是不会根据某些条件自动改变 的,一旦设置就不会发生改变。margin的取值虽然可以自动变化,但产生变化的条件一般都是空间受限。取值可以根据内容变化而变化的只有width和 height属性,前提是width、height取值为auto。下面根据块级和行内分开讨论,并在各种情况下再根据水平方向和垂直方向分开讨论。
1.块级矩形区域中:
在 水平方向上,如果width没有设置固定值即值为auto的时候,width属性有尽可能宽的倾向,比如若width和margin同时设置为auto, 那么margin的值会被浏览器重置为0。如果为width设定了固定值,那么它的值就是固定的了,不论是否脱离文档流是否超出了父元素的内容区。

1. 当width设置为auto的时候,这种情况下width有一条原则:若该内容区在布局上下文中(父元素的内容区)则它的width会尽可能的大。若该内 容区因为正的margin的原因全部超过了布局上下文,或者padding、border大于了布局上下文的宽,那么它的width取值是为0的,虽然 width为0,但内容还是存在的并且padding和border属性值也都是不会改变的。但还存在一种情况,就是当padding和margin同时 存在的时候,正数的margin-right不能使元素的矩形区域离开父元素的内容区,其数值再大也无效。而正数的margin-left可以是元素的矩 形区域离开父元素的内容区。另外一条特殊性原则就是当margin为负的情况了,负数产生的效果就是把内容区穿过布局上下文并往布局上下文外侧移动相应的 距离。当一侧margin为负数的时候,并还存在三种可能情况:另一侧margin没有数值、另一侧margin有正值、另一侧marging有负值。 另一侧margin没有值的时候,width会一直延伸。另一侧margin有正值的时候,根据正数的大小,以负数margin位置和正数margin位 置为内容区的两边,自动调整width的大小,当正数margin延伸的位置与负数margin所在的位置重合的时候,width变为0。另一侧 margin有负值的时候,内容区两边向布局上下文外侧左右延伸。
2.当width设置为固定值的时候,这种情况看起来复杂度低一些,毕竟可以自 己改变的量只剩下了margin。此时如果在内容区两边同时添加左右padding是不会有任何冲突的,但是若同时添加左右margin就很可能产生冲突 了。因为width是固定的,若固定一边的margin,那么另一边的margin也会是固定的,但另一边的固定的值很可能不是你设置的那个值。这时候这 个矩形区域就会受限,首先的处理方法就是有一个优先级,如果左右margin相互冲突,那么优先满足左边的margin,如果上下margin相互冲突, 那么优先满足上面的margin。像固定宽度的情况下两边都是负数的margin,那么元素会优先向左边移动。还有一个特殊用法是关于水平居中,即当左右 两边margin设置为auto,并且width为固定值,则该内容区域在布局上下文中水平居中。
左右负外边距还有一个特殊的规则,就是如果左外 边距为负数,并且元素都超出了浏览器,那么它就真的超出看不见了,不会在浏览器下面形成滚动条。如果右外边距为负数,并且元素向外移动超出了浏览器,那么 它是不会消失看不见的,因为会在浏览器下面形成滚动条。还有在正常情况下,汉字在父元素高度允许的情况下会自动换行,若父元素的高不足以容纳所有汉字,并 且父元素也没有设置over-flow属性,那么汉字内容就会超出父元素。而单词要想换行的话除了父元素高度要允许,还是额外设置word-wrap和 word-break属性。

在垂直方向上,垂直方向上的情况比水平方向上的还要复杂一些,因为margin在垂直方向上可以合并而在水平 方向上是不会合并的。垂直方向上的height属性不会像width一样默认情况下会尽量延展,而是会尽量虽小刚好为内容高度。这样在height为 auto,并且父元素没有adding和border空间的时候,最高块级子元素和最低块级子元素的margin范围是不会在父元素的内容区内的,它俩 margin作用范围和父元素的margin是一样的(都是父元素外的margin空间),并且有合并效果。如果父元素有border或者 padding,那么这俩空间就会阻挡最高和最低块级子元素的margin作用范围超出其布局上下文,并会增加父元素的height数值。还要注意若 padding或margin设置为了百分数,那么不管垂直还是水平方向这百分数的参照都是父元素的width。在一个布局上下文中,其中的各个块级子元 素之间的相对位置是一定的,如果一个子元素位置发生了变化,那么它下面的子元素为了维持和原来一样的相对位置也会随着产生相应的位置变化。这在负的外边距 情况下要特别注意,负的外边距根据父元素的height是否为auto表现的也不是很一样。这种不一样主要表现在最后一个块级子元素上,前面说过当 margin-top的值和margin-bottom的实现冲突的时候要优先实现margin-top的值,因为块级子元素在布局上下文中根据 margin可以*调整位置的,所以第一个和中间的子元素是不存在margin-top和margin-bottom冲突的情况的,唯一可以出现冲突的 情况就是最后一个块级子元素,因为最后一个块级子元素的margin-bottom是对父元素内容区的下边界产生作用的,如果这个下边界可变就没有冲突, 如果这个下边界不可变就可能会有冲突,这个下边界可变与不可变就是父元素的height是否为auto决定的。当height为auto的时候,块级子元 素所有的负的margin都会产生父元素变短的效果,若height为固定值,那么最后一个块级子元素的margin-bottom不会有任何作用。

2.行内矩形区域中:
行 内矩形区域(盒模型)就简单一些了。首先行内矩形区域的width和height的设置是无效的,它只会根据内容的大小自动调整自己的大小。在外边距方 面,margin的垂直设置是不会有任何效果的,因为它毕竟在行框内,margin改变不了行间距。而水平的margin是可以有相应的效果的。同样的 padding,border的垂直设置也同样是不会改变行间距的,因此看不出效果,除非有背景或者颜色会有一些效果,比如说会遮盖住相邻上下两行的内 容,但行间距还是没有改变的。水平设置倒是也同样会有相应的效果。