盒模型(Box Model)
盒模型的主要区域:内容盒子(Content Box)、内边距盒子(Padding Box)、边框盒子(Border Box)、外边距盒子(Margin Box)。
Content Box:这是内容所在的区域。此内容可以控制其父级的大小,因此通常是最可变大小的区域。
Padding Box:内边距盒子围绕内容盒子,是由 padding
属性创建的空间。如果我们的盒子设置了溢出规则,比如 overflow:auto
或者 overflow:scroll
,滚动条也会占用这个空间。
Border Box:边框盒子围绕着内边距盒子,其空间被 border
值占用。边框是盒子的边界。
Margin Box:最后一个区域,即外边距盒子,是盒子周围的空间,由盒子上的 margin
规则定义。轮廓 outline
和盒子阴影 box-shadow
等属性也占据了这个空间,因为它们被绘制在顶部,所以它们不会影响我们盒子的大小。你可以在盒子上有一个 200px
的 outline-width
,并且包括边框在内的所有内容都将是完全相同的大小。
内容和大小
盒子根据其 display
值、设置的尺寸和其中的内容具有不同的行为。
你可以通过使用外部大小来控制盒模型内容大小:width:50px;
或者,也可以继续让浏览器根据内容大小为您做出决定,使用内部大小:width: min-width;
浏览器默认行为;
当我们显式的设置宽度 width
时,由于 CSS 使用默认的 box-sizing: content-box
,宽度只会作用于内容盒子。我们也可以使用 border-box
让盒子大小为:Border Box + Padding Box + Content Box。
JS 获取盒模型大小
Content Box
JS 无法直接获取内容盒子的大小,但是可以通过 Padding Box 的大小 - Padding 得到,代码如下:
function getContentWidth (element) {
const styles = getComputedStyle(element)
return element.clientWidth
- parseFloat(styles.paddingLeft)
- parseFloat(styles.paddingRight)
}
Padding Box
内边距盒子可以通过元素的 clientWidth
只读属性获取,Element.clientWidth
可以用来获取非内联元素的内边距盒子宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。
如果元素超出了指定大小并设置了溢出规则,此时如果要获取元素的实际内边距盒子,可以通过 Element.scrollWidth
只读属性来度量包括由于溢出而在屏幕上不可见的内容的宽度。宽度的测量方式与 clientWidth 相同,如果没有溢出,则 clientWidth 和 scrollWidth 相同。
Border Box
要获取包括边框在内的宽度,可以使用元素的 offsetWidth
只读属性。HTMLElement.offsetWidth
包括任何边框、内边距和垂直滚动条(如果有的话)。它不包括伪元素的宽度,例如 ::before
或 ::after
。
也可以使用元素的 getBoundingClientRect() 属性,绝大多数情况下两者是相等的。除了在有 变换 - transform
的情况下,offsetWidth 和 offsetHeight 返回元素的布局宽度和高度,而 getBoundingClientRect() 返回渲染的宽度和高度。
例如,如果元素有 width: 100px; 和 transform: scale(0.5); getBoundingClientRect() 将返回 50 作为宽度,而 offsetWidth 将返回 100。
它们的关系如下图:
选择器(CSS Selector)
要将 CSS 应用于元素,需要先选择元素。CSS 为我们提供了许多不同的方法来执行此操作。这些选择器可以多个任意组合形成一个 CSS 规则。这些选择器包括:
基本选择器
选择所有元素。(可选)可以将其限制为特定的名称空间或所有名称空间。 语法:
*
ns|*
*|*
例子:*
将匹配文档的所有元素。按照给定的节点名称,选择所有匹配的元素。 语法:
elementname
例子:input
匹配任何 `` 元素。按照给定的
class
属性的值,选择所有匹配的元素。 语法:.classname
例子:.index
匹配任何class
属性中含有 “index” 类的元素。按照
id
属性选择一个与之匹配的元素。需要注意的是,一个文档中,每个 ID 属性都应当是唯一的。 语法:#idname
例子:#toc
匹配 ID 为 “toc” 的元素。
按照给定的属性,选择所有匹配的元素。 语法:
[attr]
[attr=value]
[attr~=value]
[attr|=value]
[attr^=value]
[attr$=value]
[attr*=value]
例子:[autoplay]
选择所有具有autoplay
属性的元素(不论这个属性的值是什么)。
分组选择器(Grouping selectors)
,
是将不同的选择器组合在一起的方法,它选择所有能被列表中的任意一个选择器选中的节点。 语法:A, B
示例:div, span
会同时匹配 元素和 元素。
组合器(Combinators)
(空格)组合器选择前一个元素的所有后代节点。 语法:
A B
例子:div span
递归匹配所有位于任意 元素之内的 元素。>
组合器选择前一个元素的直接子代的节点。 语法:A > B
例子:ul > li
匹配直接嵌套在 元素内的所有 元素。一般兄弟组合器(General sibling combinator)
~
组合器选择兄弟元素,也就是说,后一个节点在前一个节点后面的任意位置,并且共享同一个父节点。 语法:A ~ B
例子:p ~ span
匹配同一父元素下, 元素后的所有 元素。紧邻兄弟组合器(Adjacent sibling combinator)
+
组合器选择相邻元素,即后一个元素紧跟在前一个之后,并且共享同一个父节点。 语法:A + B
例子:h2 + p
会匹配所有紧邻在 元素后的 元素。||
组合器选择属于某个表格行的节点。 语法:A || B
例子:col || td
会匹配所有 作用域内的 元素。
伪选择器(Pseudo)
:
伪选择器支持按照未被包含在文档树中的状态信息来选择元素。 例子:a:visited
匹配所有曾被访问过的 元素。::
伪选择器用于表示无法用 HTML 语义表达的实体。 例子:p::first-line
匹配所有 元素的第一行。
使用技巧
- 使用紧邻兄弟组合器为堆叠元素之间添加间隙。
> * + *
仅当元素是 .top
的子元素的下一个兄弟元素时,才使用紧邻兄弟组合器添加间隙,并使用伪元素为间隙添加背景色。
也可以使用
column-gap
、row-gap
为 Multi-column(多列布局)、Flexible Box(弹性盒子)以及 Grid layouts(网格布局)中的列或行之间添加间隙。
- 使用 Emmet 类 CSS 选择器语法快速书写结构化代码块
例如使用下面的缩写:
#page>div.logo+ul#navigation>li*5>a{Item $}
将转换为:
<div id="page">
<div class="logo"></div>
<ul id="navigation">
<li><a href="">Item 1</a></li>
<li><a href="">Item 2</a></li>
<li><a href="">Item 3</a></li>
<li><a href="">Item 4</a></li>
<li><a href="">Item 5</a></li>
</ul>
</div>
大多数编辑器都支持 Emmet 缩写 Tab 键快速展开代码块,它的语法不限于 HTML,详情查看官方文档:Emmet Documentation
层叠
了解层叠算法有助于了解浏览器如何解决样式冲突。层叠算法分为 4 个不同的阶段。
- 资源顺序及位置
- 优先级 - 权重(Specificity)
- 来源(Origin)
- 重要性(!important)
资源顺序及位置
样式可以来自 HTML 页面上的各种来源,例如 <link>
标记、嵌入的 <style>
标记和元素 style
属性中定义的内联 CSS。
<link>
或 <style>
中的样式,靠后的顺序优先级更高,内连 style
CSS 会覆盖其它两者的样式而不管它们的顺序,除非他们定义了 !important
属性。
优先级
本质上,不同类型的选择器有不同的分数值,把这些分数相加就得到特定选择器的权重,然后就可以进行匹配。
一个选择器的优先级可以说是由四个部分相加,可以认为是个十百千 — 四位数的四个位数:
- 千位: 如果声明在
style
的属性(内联样式)则该位得一分。这样的声明没有选择器,所以它得分总是1000。 - 百位: 选择器中包含ID选择器则该位得一分。
- 十位: 选择器中包含类选择器、属性选择器或者伪类则该位得一分。
- 个位:选择器中包含元素、伪元素选择器则该位得一分。
注: 通用选择器 (
*
),组合符 (+
,>
,~
,' '
),和否定伪类 (:not
) 不会影响优先级。
来源
层叠考虑了不同来源的 CSS,而不止我们编写的样式。此来源包括浏览器的内部样式表、浏览器扩展或操作系统添加的样式以及我们编写的 CSS。下面列出这些来源的优先级,从低到高:
- 用户代理基本样式 - User Agent,浏览器作为用户的代理,会帮我们设置默认应用于 HTML 元素的样式。
- 用户样式 - Local User,浏览器的用户所具有的样式,这些可以来自操作系统级别,例如基础字体大小。也可以来自浏览器扩展。
- 作者样式 - Authored CSS,我们程序员为网站编写的 CSS。
- 作者样式表中的
!important
声明,我们添加到您编写的声明中的任何!important
。 - 用户样式表中的
!important
声明,来自操作系统级别或浏览器扩展级别 CSS 的任何!important
。 - 用户代理样式表中的
!important
声明,浏览器提供的默认 CSS 中定义的任何!important
。
重要性
重要性从低到高依次如下:
- 普通类型的规则
animation
类型规则!important
类型规则transition
类型规则
继承
每个 HTML 元素都有一些默认定义的 CSS 属性,并带有初始值。初始值是未继承的属性,如果级联无法计算该元素的值,则使用初始值显示默认样式。如果用户为父元素定义了样式,则会级联继承给子元素,但并不是所有 CSS 属性都是可以继承的,这是可继承属性的完整列表,参考 W3:
- azimuth
- border-collapse
- border-spacing
- caption-side
- color
- cursor
- direction
- empty-cells
- font-family
- font-size
- font-style
- font-variant
- font-weight
- font
- letter-spacing
- line-height
- list-style-image
- list-style-position
- list-style-type
- list-style
- orphans
- quotes
- text-align
- text-indent
- text-transform
- visibility
- white-space
- widows
- word-spacing
控制继承
设置该属性会使子元素属性和父元素相同。实际上,就是 “开启继承”,使用该属性可以让元素使用继承值而不是浏览器默认值。
设置属性值和浏览器默认样式相同。如果浏览器默认样式中未设置且该属性是可继承的,那么会设置为 inherit
。
将属性重置为自然值,也就是如果属性是可继承的那么就是 inherit
,否则和 initial
一样。