Mr. Panda
Tech For Fun

[CSS] CSS 世界【读书笔记】(1)

这篇文章是张鑫旭 css 世界的阅读笔记之一。本文涉及的章节为流、 元素与基本尺寸和盒尺寸四大家族,主要内容包括块元素、内联元素的原理、width/height 的原理,content、padding、margin、border 的原理和应用。

流、 元素与基本尺寸

块级元素

“块级元素”和“display 为 block 的元素”不是一个概念。例如,li 元素默认的 display 值是 list-item, table 元素默认的 display 值是 table,但是它们均是“块级元素”,因为它们都符合块级元素的基本特征,也就是一个水平流上只能单独显示一个元素,多个块级元素则换行显示。

IE 浏览器不支持伪元素的 display 值为 list-item。这是不使用 display:list-item 清除浮动的
主因,兼容性不好。因为所有的“块级元素”都有一个“主块级盒子”, list-item 除此之外还有一个“附加盒子”,学名“标记盒子”(marker box),专门用来放圆点、数字这些项目符号,所以list-item 元素会出现项目符号。

每个元素都两个盒子,外在盒子和内在盒子(容器盒子)。外在盒子负责元素是可以一行显示,还是只能换行显示;内在盒子负责宽高、内容呈现什么的。block 的元素的盒子实际由外在的“块级盒子”和内在的“块级容器盒子”组成,值为 inline-block 的元素则由外在的“内联盒子”和内
在的“块级容器盒子”组成,值为 inline 的元素则内外均是“内联盒子”。

width/height

width/height 4 种不同的宽度表现:

  • 充分利用可用空间(fill-available):元素的宽度默认是 100%于父级容器的。
  • 收缩与包裹(shrink-to-fit):典型代表就是浮动、绝对定位、 inline-block 元素或 table 元素。
  • 收缩到最小(min-content):这个最容易出现在 table-layout 为 auto 的表格中。
  • 超出容器限制(max-content):除非有明确的 width 相关设置,否则上面 3 种情况尺寸都不会主动超过父级容器宽度的,但是存在一些特殊情况,如 white-space:nowrap。

在 CSS 世界中,盒子分“内在盒子”和“外在盒子”,显示也分“内部显示”和“外部显示”,尺寸也分“内部尺寸”(Intrinsic Sizing)和“外部尺寸”(Extrinsic Sizing)。其中“内部尺寸”表示尺寸由内部元素决定;“外部尺寸”,宽度由外部元素决定。

外部尺寸与流体特性:

  • 正常流宽度:所谓流动性,是一种 margin/border/padding/content 内容区域自动分配水平空间的机制。
  • 格式化宽度:在默认情况下,绝对定位元素的宽度表现是“包裹性”,宽度由内部尺寸决定。对于非替换元素,当 left/right 或 top/bottom 对立方位的属性值同时存在的时候,元素的宽度表现为“格式化宽度”,其宽度大小相对于最近的具有定位特性(position 属性值不是 static)的祖先元素计算。格式化宽度”具有完全的流体性。

内部尺寸与流体特性:

  • 包裹性(自适应性):所谓“自适应性”,指的是元素尺寸由内部元素决定,但永远小于“包含块”容器的尺寸(除非容器尺寸小于元素的“首选最小宽度”)。
  • 首选最小宽度:所谓“首选最小宽度”,指的是元素最适合的最小宽度。在 CSS 世界中,图片和文字的权重要远大于布局。东亚文字(如中文)最小宽度为每个汉字的宽度;西方文字最小宽度由特定的连续的英文字符单元决定;类似图片这样的替换元素的最小宽度就是该元素内容本身的宽度。
  • 最大宽度:最大宽度就是元素可以有的最大宽度。如果内部没有块级元素或者块级元素没有设定宽度值,则“最大宽度”实际上是最大的连续内联盒子的宽度。“连续内联盒子”指的全部都是内联级别的一个或一堆元素,中间没有任何的换行标签或其他块级元素。

为何 box-sizing 不支持 margin-box?

margin 并不会改变元素尺寸(offsetWidth)。margin 只有在 width 为 auto 的时候可以改变元素的尺寸,但是,此时元素已经处于流动性状态,根本就不需要 box-sizing。对于 box-sizing 的 margin-box 效果, 如果是 IE10 及以上版本浏览器,可以试试 flex 布局, 如果要兼容 IE8 及以上版本可以使用“宽度分离”。

如何评价*{box-sizing:border-box}?

  • 产生没必要的消耗。 通配符*应该是一个慎用的选择器,因为它会选择所有的标签元素。对于普通内联元素(非图片等替换元素), box-sizing 无论是什么值,对其渲染表现都没有影响,因此, *对这些元素而言就是没有必要的消耗;同时有些元素,如 search 类型的搜索框,其默认的 box-sizing 就是 border-box(如果浏览器支持),因此, *对 search 类型的而言也是没有必要的消耗。
  • 不能解决所有问题。 box-sizing 不支持 margin-box,只有当元素没有水平 margin 时候, box-sizing 才能真正无计算,而“宽度分离”等策略则可以彻底解决所有的宽度计算的问题。
  • box-sizing 被发明出来最大的初衷应该是解决替换元素宽度自适应问题。在 CSS 世界中,唯一离不开 box-sizing:border-box 的就是原生普通文本框和文本域的 100%自适应父容器宽度。拿文本域举例, <textarea>为替换元素,替换元素的特性之一就是尺寸由内部元素决定且无论其 display 属性值是 inline 还是 block。这个特性很有意思,对于非替换元素,如果其 display 属性值为 block,则会具有流动性,宽度由外部尺寸决定,但是替换元素的宽度却不受 display 水平影响。我们只能通过 width 设定让尺寸 100%自适应父容器。那么,问题就来了,<textarea>是有 border 的,而且需要有一定的 padding 大小,否则输入的时候光标会顶着边框,体验很不好。于是, width/border 和 padding 注定要共存,同时还要整体宽度 100% 自适应容器。如果不借助其他标签,肯定是无解的。
input, textarea, img, video, object {
 // 将替换元素的设置为 border-box 
 box-sizing: border-box;
}

height:auto 要比 width:auto 简单而单纯得多,因为 CSS 的默认流是水平方向的,宽度是稀缺的,高度是无限的。因此,宽度的分配规则就比较复杂,高度就显得比较随意。height:auto 在绝对定位模型中“格式化高度”时也有外部尺寸特性。

height 和 width 还有一个比较明显的区别就是对百分比单位的支持。 对于 width 属性,就算父元素 width 为 auto,其百分比值也是支持的;但是,对于 height 属性,如果父元素 height 为 auto,只要子元素在文档流中,其百分比值完全就被忽略了。在设置一个元素高度为 100% 时,一定要保证其祖先元素高度都不能为 auto,否则就不会生效。

父级为auto时为何 height:100%无效而width:auto生效?

  • 浏览器按照从上而下、自外而内的顺序渲染 DOM 内容。对于宽度,先渲染父元素,后渲染子元素,是有先后顺序的。因此,当渲染到父元素的时候,子元素的 width:100%并没有渲染,宽度就是图片加文字内容的宽度;等渲染到文字这个子元素的时候,父元素宽度已经固定,此时的 width:100%就是已经固定好的父元素的宽度。宽度不够怎么办?溢出就好了, overflow 属性就是为此而生的。
  • height 在规范中,如果包含块的高度没有显式指定(即高度由内容决定),并且该元素不是绝对定位,则计算值为 auto。 auto 和百分比计算,肯定是算不了 NaN。
  • width 在规范中,如果包含块的宽度取决于该元素的宽度,那么产生的布局在 CSS 2.1 中是未定义的。对于 “未定义行为”,浏览器可以自己根据理解去发挥,好在布局效果在各个浏览器下都是一致的。
  • 高度明确了就是 auto,高度百分比计算自然无果, width 却没有这样的说法,因此,就按照包含块真实的计算值作为百分比计算的基数。
  • 如何让元素支持 height:100%效果?设定显式的高度值;设定显式的高度值。

min-width/max-width和min-height/max-height

min-width/min-height 的初始值是 auto, max-width/maxheight的初始值是none。

  • max-*的初始值不能为 auto的原因是:max-width 会覆盖 width,这回造成 width永远不能设置为比 auto 计算值更大的宽度值了,这显然是有问题的。
  • min-*的初始值为 auto 是因为:min-wdith/height 值为 auto 合法,通过 document.body.style.minWidth 能获取到值为 auto;数值变化无动画,直接给min-*设置 transition 动画时,动画并不会生效,这就说明 min-*的初始值并不是 0。

CSS 世界中, min-width/max-width 和 min-height/max-height 属性间,以及与
width 和 height 之间有一套相互覆盖的规则:超越!important, 超越最大,也就是这种覆盖的优先级要高于 !important。超越最大指的是min-width覆盖max-width,此规则发生在min-width和max-width 冲突的时候(注意不是“后来居上”规则)。

max-height 配合 transition 实现元素展开收起动画:max-height 使用足够安全的最小值,因为如果过大的话会造成收起时有一定的动画延迟。这个原理上正是利用了了max-height 和 height 的覆盖关系。

内联元素(inline-level elements)

“外在盒子”有 inline、 block 和 run-in 三种水平。其中 run-in 鲜有人使用,且有淘汰风险,可以忽略;剩下的 inline 和 block 几乎瓜分了剩下的全部江山, 是流体布局的本质所在。从作用上来讲,块级负责结构,内联负责内容。 CSS 世界是为图文展示而设计的。所谓图文,指图片和文字,是最典型的内联元素。所以,在 CSS 世界中,内联元素是最为重要的,涉及的 CSS 属性也非常之多,这些 CSS 属性往往具有继承特性,混合在一起会导致 CSS 解析规则非常复杂。这就是内联元素的解析比块级元素解析更难理解的原因——其是多个属性共同作用的结果。

内联元素”的“内联”特指“外在盒子”,指的是元素会在一行显示,display 的值通常为 inline、inline-block 和 inline-table。display 默认值是 inline-block。浮动元素虽然也可以一行显示,但是他已经脱离了文档流,不是内联元素。

内联盒模型:

  • 内容区域(content area)。内容区域指一种围绕文字看不见的盒子,其大小仅受字符本身特性控制,本质上是一个字符盒子(character box);但是有些元素,如图片这样的替换元素,其内容显然不是文字,不存在字符盒子之类的,因此,对于这些元素,内容区域可以看成元素自身。内容区域是“看不见的”,但是可以把文本选中的背景色区域作为内容区域,文本选中区本质上就等同于基本盒尺寸中的 content box。
  • 内联盒子(inline box)。“内联盒子”不会让内容成块显示,而是排成一行,这里的“内联盒子”实际指的就是元素的“外在盒子”,用来决定元素是内联还是块级。该盒子又可以细分为“内联盒子”和“匿名内联盒子”两类。如果外部含内联标签(span,em等),则属于“内联盒子”;如果只是文字,则属于“匿名内联盒子”。
  • 行框盒子(line box)。每一行就是一个“行框盒子”,由“内联盒子”组成。
  • 包含盒子(containing box)(包含块 containing block)。p 标签就是一个包含盒子,由行框盒子组成。

幽灵空白节点(strut 支柱):指的是在 HTML5 文档声明中,内联元素的所有解析和渲染表现就如同每个行框盒子的前面有一个永远透明,不占据任何宽度,看不见也无法通过脚本获取“空白节点”一样。幽灵空白节点”实际上是个假想盒子,是一个存在于每个“行框盒子”前面,同时具有该元素的字体和行高属性的 0 宽度的内联盒

盒尺寸四大家族

content

content 与替换元素

根据元素是否具有可替换内容,我们也可以把元素分为替换元素和非替换元素。通过修改某个属性值呈现的内容就可以被替换的元素就称为“替换元素”,如 img、video、iframe、textarea、input、select等。所有的替换元素都是内联水平元素,替换元素默认的 display 值却是不一样。

替换元素的特性:

  • 内容的外观不受页面上的 CSS 的影响(样式表现在 CSS 作用域之外)。更改替换元素本身的外观需要类似 appearance 属性,或者浏览器自身暴露的一些样式接口。
  • 有默认的尺寸。如 video/canvas/iframe 默认的尺寸是 300px*150px,img 的默认尺寸是 0,表单元素的替换元素默认尺寸和浏览器有关。
  • 在很多 CSS 属性上有自己的一套表现规则。如vertical-align 的默认值的 baseline,但是替换元素的基线定义成元素的下边缘。

替换元素的尺寸计算规则:

  • 缺省尺寸:最终宽度表现为 300 像素,高度为 150 像素, 宽高比 2:1。这条规则适用于 video、canvas、iframe,不适用于 img,因为img 的缺省尺寸因浏览器而异。
  • 固有尺寸:替换内容原本的尺寸。
  • HTML 尺寸:只能通过HTML 原生属性改变,这些 HTML 原生属性包括img的 width 和 height 属性、input 的 size 属性、textarea 的 cols 和 rows 属性等。
  • CSS 尺寸。
  • 优先级:CSS 尺寸 > HTML 尺寸 > 固有尺寸 > 缺省尺寸。

在给img设置宽高之所以能改变img的宽高,是因为img的object-fit:fill(img和其他一些替换元素的替换内容的适配方式可以通过object-fit 属性修改),并不是改变了img的固有尺寸,固有尺寸是不可改变的。

替换元素和非替换元素之间只隔了一个 src 属性

如果我们把img 的 src 属性去掉, 其实就是一个和 span 类似的普通的内联标签,也就是成了一个非替换元素,IE 浏览器下没有 src 属性还是完全的替换元素,因为IE 浏览器中有个默认的占位替换内容,当 src 属性缺失的时候,会使用这个默认的占位内容。

替换元素和非替换元素之间只隔了一个 CSS content 属性:

  • 替换元素之所以为替换元素,就是因为其内容可替换,而这个内容就是 content box,对应的 CSS 属性是 content。content 属性决定了是替换元素还是非替换元素。
  • 在 Chrome 浏览器下,所有的元素都支持 content 属性,而其他浏览器仅在::before/::after 伪元素中才有支持。
  • 如果图片原来是有 src 地址的,我们也是可以使用 content 属性把图片内容给置换掉。但是content 属性改变的仅仅是视觉呈现,当我们以右键或其他形式保存这张图片的时候,所保存的还是原来 src 对应的图片,屏幕阅读设备阅读的和搜索引擎 SEO抓取的还是原始内容,对页面的可访问性等没有任何影响。使用 content 生成图片,我们是无法设置图片的尺寸的,可以使用svg图片。

content 属性生成的对象称为“匿名替换元素”(anonymous replaced element)。使用 content 生成的文本是无法选中、无法复制的,同时无法被屏幕阅读设备读取,也无法被搜索引擎抓取,对可访问性和 SEO 都很不友好, content 属性只能用来生成一些无关紧要的内容,如装饰性图形或者序号之类。content 不能左右:empty 伪类。content 动态生成值无法获取。

padding

内联元素 padding 对视觉层和布局层具有双重影响,所有类似“垂直方向 padding 对内联元素没有作用”的说法是不正确的。对于非替换元素的内联元素,不仅 padding 不会加入行盒高度的计算, margin和 border 也都是不计算高度,但实际上在内联盒周围发生了渲染。这种特性的应用:在不影响当前布局的情况下,优雅地增加链接或按钮的点击区域大小;实现高度可控的分隔线;锚点定位距离顶部一段距离。

padding 属性是不支持负值;padding 支持百分比值,但是无论是水平方向还是垂直方向均是相对于宽度计算的。这种设计可以让我们轻松实现自适应的等比例矩形效果。百分比值如果应用在内联元素上,表现如下:相对于宽度计算;默认的高度和宽度细节有差异;padding 会断行。内联元素的垂直 padding 会让“幽灵空白节点”显现,由于内联元素默认的高度完全受 font-size 大小控制,可以通过font-size:0让内联元素的高度为 0。

margin

对于 padding,元素设定了 width 或者保持“包裹性”的时候,会改变元素可视尺寸;但是
对于 margin 则相反,元素设定了 width 值或者保持“包裹性”的时候, margin 对尺寸没有
影响,只有元素是“充分利用可用空间”状态的时候, margin 才可以改变元素的可视尺寸。只要元素的尺寸表现符合“充分利用可用空间”,无论是垂直方向还是水平方向,都可以通过 margin 改变尺寸。CSS 世界默认的流方向是水平方向,因此,对于普通流体元素, margin 只能改变元素水平方向尺寸;但是,对于具有拉伸特性的绝对定位元素,则水平或垂直方向都可以,因为此时的尺寸表现符合“充分利用可用空间”。

Chrome 浏览器是子元素超过 content box 尺寸触发滚动条显示,而 IE 和 Firefox 浏览器是超过padding box 尺寸触发滚动条显示。只能使用子元素的 margin-bottom 来实现滚动容器的底部留白。

和 padding 不同,内联元素垂直方向的 margin 是没有任何影响的,既不会影响外部尺寸,
也不会影响内部尺寸。

margin 的百分比值无论是水平方向还是垂直方向都是相对于宽度计算的。元素设置 margin 在垂直方向上无法改变元素自身的内部尺寸,往往需要父元素作为载体,此外,由于 margin 合并的存在,垂直方向往往需要双倍尺寸才能和 padding 表现一致。

块级元素的上外边距( margin-top)与下外边距(margin-bottom)有时会合并为单个外边距,这样的现象称为“margin 合并”(和当前文档流方向的相垂直的方向)。

  • 相邻兄弟元素 margin 合并:让图文信息的排版更加舒服自然。
  • 父级和第一个/最后一个子元素:在页面中任何地方嵌套或直接放入任何裸,都不会影响原来的块状布局。
  • 空块级元素的 margin 合并:可以避免不小心遗落或者生成的空标签影响排版和布局。

margin 合并的计算规则:“正正取大值”“正负值相加”“负负最负值”。

margin:auto 的填充规则如下。

  • 如果一侧定值,一侧 auto,则 auto 为剩余空间大小。
  • 如果两侧均是 auto,则平分剩余空间。

margin:auto 无法垂直居中的原因:触发 margin:auto 计算有一个前提条件,就是 width 或 height 为 auto 时,元素是具有对应方向的自动填充特性的。

设置的margin 无效的情形:

  • display 计算值 inline 的非替换元素的垂直 margin 是无效的,虽然规范提到有渲染,但浏览器表现却未寻得一点踪迹,这和 padding 是有明显区别的。对于内联替换元素,垂直 margin 有效,并且没有 margin 合并的问题,所以图片永远不会发生 margin 合并。
  • 表格中的和元素或者设置 display 计算值是 table-cell 或 table-row 的元素的 margin 都是无效的。但是,如果计算值是 table-caption、 table 或者 inline-table 则没有此问题,可以通过 margin 控制外间距,甚至::first-letter 伪元素也可以解析 margin。
  • margin 合并的时候,更改 margin 值可能是没有效果的。
  • 绝对定位元素非定位方位的 margin 值“无效”。这里不是真正的无效,而是增加了外部尺寸。
  • 定高容器的子元素的 margin-bottom 或者宽度定死的子元素的 margin-right 的定位“失效”。若想使用 margin 属性改变自身的位置,必须是和当前元素定位方向一样的 margin 属性才可以,否则, margin 只能影响后面的元素或者父元素。
  • 鞭长莫及导致的 margin 无效。
  • 内联特性导致的 margin 无效。

border

border-width 却不支持百分比,支持若干关键字,包括 thin(1)、 medium(默认值,3)
和 thick(4)。

border-color 默认颜色就是color 色值。

Jonsam

一个理科IT宅男,喜欢旅游、分享和美食,做点想做的事情,遇见想见的人。

🍒 美食 | 🌐 FE | 🕌 旅行 | 💻 加班 | ♍ 处女座

jonsam ng

jonsam ng

文章作者

海阔凭鱼跃,天高任鸟飞。

[CSS] CSS 世界【读书笔记】(1)
这篇文章是张鑫旭 css 世界的阅读笔记之一。本文涉及的章节为流、 元素与基本尺寸和盒尺寸四大家族,主要内容包括块元素、内联元素的原理、width/height 的原理,content、padding、margi…
扫描二维码继续阅读
2021-10-01