虽说实际编程水平是才最重要的,但是夯实基础,才能走的更远。仿生狮子最近在准备面试,这个礼拜看了一堆 CSS 相关的内容。整理了数十万字资料后,给你带来这些 CSS 知识点分享。
阅读本文(收藏本文)你将串联以下关键词:
世界宽广而宏大,我们的知识只是汪洋里的一叶扁舟。组织语言,用博客和书本输出内容,自然受到语言这种媒介的限制。尽管经过悉心整理,线性的文字不可能把每一个知识点都结网一般从头到尾串联起来。要是大家能灵活使用搜索引擎,遨游互联网,突破平面的限制,构建自己的知识骨架,那是再好不过。文中内容比较基础,大部分内容扫一下就阔,如果一点儿印象也没的,还是要看看文档详细了解,最好编码实践一下——写代码和玩乐器一样,都属于动态学习的范畴,也就是说,边学边实践能更容易地将概念融会贯通。
我曾因在掘金某篇文章评论区看到有位老哥因为看不懂文章中讲述的概念而觉得“(文章)写的乱七八糟,不清不楚”,觉得很是心酸。对知识的深入了解不能抛开概念不谈,这和对概念的理解或是编码实操的能力是不同的。它们相互补充的,对学习也有所帮助。不要因为看了代码就不敲代码了,也不要因为能敲代码就不去了解概念了。这里提一些比较重要的 CSS 技术名词(或概念),做个开头吧。
规则,由属性和值组成的声明、声明与括号形成的声明块再加上选择器组成,而一条或多条规则组合成了规则集。
元素是用来组织文档结构的基础,比如 p、span 等。每个元素都会对文档的表现起一定作用,每个元素都会在浏览器中以矩形盒子的形式出现。
尽管不能直接查看页面上的盒子的外边界,但你可以打开控制台,输入这行代码,给页面上所有的元素设置边框样式,一览其排列:
$$('*').map(x => x.style.border = '1px solid')复制代码
类似 div、ul、p、h1 这些都是块级元素。一般来说,块级元素会生成一个默认填满父容器的内容区域。其后的兄弟元素将会被“换行显示”;内联元素会在一个文本行中生成元素框,它不会打断这行文本。内联元素常也译为“行内级元素”,在一些文章中,也被称为“行内元素”。
除了如 img、input、iframe 等元素,大多数元素都是非替换元素。替换元素的内容区域会被替换为其指向的外部对象。比如,如果 img 元素不带 src 属性的话,它不指向任何内容,在文档中没有意义,也就不是替换元素;如果 img 的 src 链接了外部图片,那么它的内容区域将被图片替换,此时就是替换元素。
元素会依据其 Display 类型参与布局。主要取决于两种特征:内在盒子 和 外在盒子。内在盒子通常也称作“容器盒子”,和外在盒子相比,前者描述了元素跟其后代之间的行为,而后者描述了元素与其父元素和兄弟元素之间的行为。
CSS 中有哪些常用的选择器呢?
有哪些常用的伪类/伪元素呢?
伪类和伪元素在选择器中用一个冒号还是两个冒号连接?它们有什么区别呢?... 这里提到了伪类和伪元素,分享一下我的理解思路,我觉得还是蛮好使的:
前阵子看一篇文章,有一位老哥觉得在 HTML 中给空列表加一个“空”的占位不好,导致“布局混乱信息难懂”,所以可以使用 :empty 伪类把占位逻辑往 CSS 转移的:
<dl> <dt>姓名:</dt> <dd>张三</dd> <dt>性别:</dt> <dd></dd> <dt>手机:</dt> <dd></dd> <dt>邮箱:</dt> <dd></dd></dl><style>dd:empty::before { content: '暂无'; color: gray;}</style>复制代码
这只能体现 CSS 的可能性,但在项目中运用则是一种错误的做法。比如,如果考虑项目后期要做国际化,那么写在 CSS 中这些内容的可维护性就非常差了。CSS 是负责样式的语言,千万别混淆了 CSS 处理样式,JS 处理逻辑这两种不同的思维形式。
特殊性(Specificity),又称作:优先级、特异性,即浏览器对应用到同一个元素的同种声明的重视程度。选择器的特殊性由选择器的组成确定。特殊性质表述为 4 个数字组成的统一的部分,如: [0,0,0,0]。比较特殊性值表时,同索引左侧位数较大者特殊性高,如 [1,0,0,0] 大于 [0,99,0,0]
试着比较一下 #container .card .card-body a.graph:hover:active、#container #card-graph-con a.graph:hover 这两个个选择器的特殊性吧?
我们不必事无巨细地给每一个元素都加上声明,对于某些值,子元素会继承父元素的值。CSS世界的诞生就是为图文信息展示服务的,所以 CSS 中的继承理念很符合我们的直觉。大部分属性如边框、边距、填充等盒子的样式不能继承,而文字相关的如字体、颜色等属性就可以继承。
可继承的属性以下列出:
有一点需要注意的是,text-decoration-* 属性看起来会被继承,但实则不然:
<p style="text-decoration: underline;"> 父元素 | <span style="text-decoration: line-through;"> 子元素 text-decoration: line-through </span> | 父元素</p>复制代码
父元素 | 子元素 text-decoration: line-through | 父元素
需要额外注意的一点是,继承得来的值得特殊性要比 [0,0,0,0] 低。假设有以下代码:
<h1>This is my heading.<span>asdf</span></h1><style>* { color: #bbb;}h1 { color: #333; }</style>复制代码
将会得到以下结果:
此外,《CSS权威指南》中提到:应用到 body 元素得背景样式可以(向上)传递到 html 元素,相应地可以定义其画布,这点属于继承中的特例。但我没能复现。
浏览器会如何应用两个特殊性相等的规则呢?级联(又作:层叠)会给出答案。这里给出一个有关于级联与样式来源的权重的参考,按权重递减排列:
附:在我的 Yandex 浏览器里已经找不到显式设置用户样式的入口了,我也相信普通用户不会知晓如何设置用户样式的方法,就算知晓也不会去使用——毕竟,插件要香一些。
“LVHT”指的是我们写 CSS 时常使用的“a:link、a:visited、a:hover、a:active”顺序,主要是依靠级联的最后一条规则“声明出现在文档中的顺序越后,权重越大”。
一般来说,我们不会使用“a:active:hover、a:hover:link”等选择器。由于我们总是这四个状态的重要性依次递增,所以推荐“LVHT”顺序,也可以记为“Love&Hate”。
从“LVHT”这个例子可以看出我为什么觉得“级联”是CSS最吸引人的地方。级联概念能体现出一种简单但深刻的理念——设计人员总是希望用户在浏览器中与网页交互得到正确的反馈,就好比悬浮按钮时有样式反馈,点击按钮时有激活提示——级联迫使设计人员(开发者)思考“我们能提供什么信息”、“我们鼓励用户做什么”。
VFM,即“Visual Formatting Model”,视觉格式化模型。决定了浏览器如何处理文档树,将元素转化为盒子,及如何布局。如果对 VFM 不清楚,往往不能理解浏览器中很多看起来怪异(常常被说是“BUG”)的布局表现。其实这些怪异的表现不是“BUG”,只是尽管 VFM 是一种开放而强大的模型,它充满细节,和我们脑海中的布局常识可能不一致;同时,又留有一些规范未覆盖的余地,供浏览器自由发挥。
页面上的布局受到这些因素的影响:
元素根据 VFM 生成矩形盒子时,需要通过盒模型规范盒子的尺寸。
一般情况下,CSS 使用自上而下,从左至右的布局。盒子是其中的最基础的渲染单位,代表了元素的展现方式,以及它们同周围元素的相互作用。渲染时,所有元素都会依据盒模型来判定其大小,位置以及属性。
通常我们会觉得 Width 属性定义了一个元素(块级元素)的宽度,这种说法其实不太准确。Width 定义的是“内容区”的宽度。一般来说,内容区的宽度指下图的 width,
第一张图是 W3C 标准盒模型,第二张是 IE 盒模型。至于为啥有两种标准,这得追溯回...
...很久以前... IE:我觉得盒模型应该是这样的,blahblah。 W3C:明显应该是这样的才对,blahblah。 结果是 IE 在怪异模式下用了「不标准」的盒模型,而标准模式下用了「标准」的盒模型。 围观群众:听说 IE 的盒模型不标准。 ...多年过去... W3C:感觉还是 IE 的那个模型比较好。但我们已经回不去了... 算了加个属性支持一下 IE 那种模式吧。 顾轶灵@知乎: box-sizing 这货就是用来擦屁股的...
以前写项目的时候,我喜欢使用通配符重置所有元素的 Box Sizing 为 Border Box,这样会使样式编写容易一些,因为 Border Box 包含了 Border 的宽度,这个宽度各符合直觉。不过,有几点可能需要考虑:
《CSS世界》提到:属性 Box Sizing 发明的初衷可能是用于解决替换元素的宽度自适应的问题。比如输入框的宽度默认情况下的 100% 往往会超过父容器。所以建议这样写样式重置:
input, textarea, img, video, object { box-sizing: border-box; }复制代码
盒子的类型对应元素的 Display 属性。
想象一下,我们在样式表中编写的 display: inline-block 声明,能使元素在行内显示,而内部布局依旧类似块级元素。它其实是一种省略式写法,可以理解为这条声明只规定了外在盒子显示为 Inline,而内在盒子则会回退为默认值 Block。将这条声明补全则成了 display: inline-block block。
以下是我们常用的简写对应的默认值:
Short display Full display Block Block Flow Inline Inline Flow Inline Block Inline FlowRoot Flex Block Flex
一个显示内容类型的元素(display: contents)会在格式化从布局树中将自身移除,但是它的内容却得以保留。请联想一下 Vue 内置的 Template 标签,或是 React.Fragment,大概类似那种玩意儿...
需要注意以下几点:
显示内容类型的元素 ::before 和 ::after 两个伪元素都将得到保留。
定位方案就三种,普通流定位,浮动定位或是绝对定位。
可以使用相关属性改变流的方向,下图是阿拉伯使用的搜索引擎主页,见 HTML 元素上定义的 DIR 属性:
布局时,会依据盒子的类型生成 BFC(Block Formatting Context) 或是 IFC(Inline Formatting Context),它们使盒子在界面上形成一个独立的,不影响外界的容器。
最直观的感受肯定是 HTML 元素,即根元素,它是浏览器中最重要的一个独立的不影响外界的容器。根元素会创建 BFC,在一些情况下,其它元素也会。这里有 MDN 的一份创建 BFC 方法的清单:
BFC 在布局时,会应用以下规则,需要注意:
BFC 不会和外部浮动元素重叠
BFC 内部的相邻的块级盒子的垂直外边距会折叠
计算 BFC 的高度时,内部浮动的盒子也会参与计算
如果一个块级盒内部只有行内盒子,那么会创建一个行内上下文,其内部的盒子按照 IFC 的规则水平排列。
在 IFC 中,需要注意以下问题:
Float 虽然平常用的不多,但是这里单独拎出来康康。原理至少还是要了解的,因为浮动涉及到一些“看起来像 BUG”的问题。
浮动元素,尤其是浮动的图片,很适合用来进行图文混合排版。但是由于脱离了正常文档流,非 BFC 容器不会计算内部浮动元素的高度,所以会引起父容器塌陷的问题。下一小节将会介绍清除浮动的一些办法。
HTML 中两个向右浮动的元素,分别为 ONE 和 TWO,在浏览器渲染出来确实 ONE 在右侧,TWO 在左侧。
一个浮动的元素会脱离正常的文档流,然后依据 Float 属性向左或者向右浮动,直到碰到父容器的边框或者另一个浮动元素的边框为止。所以,浮动错位问题是“特性”,不是“BUG”...
有几种常见的解决塌陷问题的方法,记录如下:
VFM 概念中除了有 BFC、IFC、TFC(Table Formatting Context),CSS3 中还新增了 FFC(Flex Formatting Context)和 GFC(Grid Formatting Context),因为可扩展讲的内容太多了,这里暂且只简单提一下 FFC。
应用了 display: flex | inline-flex 的元素将会成为 Flex 容器,Flex 容器会创建 FFC 以格式化布局其子项。我们常说的“Flex 布局”,就是指“FFC”。
FFC 是一种非常灵活的一维布局方式(GFC 则是强大的二维布局方式),它使子元素按照主轴方向排列,并可设置不同轴向的对齐方式。
Flex 容器可以应用的属性
Flex 容器的子元素可以应用的属性
flex-grow 光看概念有些难懂,这里用数值说明一下:假设父容器宽度 100px,子元素只有1个,宽度为 50px,flex-grow: .5,那么,子元素就会占据 100px-50px 剩余空间的“自身 flex-grow 数值 / 所有子项 flex-grow 之和”的值的百分比(.5 / .5 = 100%)。
所以,当父容器只含有一个子元素时,给子元素设置 flex-grow: 1 能使它填充满整个父容器:
<div class="b1" stype="display: flex; height: 50px;"> <div class="bb p010 bg-gray tac" style="margin: 1px; border: 1px dashed; flex-grow: 1; height: 50px; line-height: 50px;">DASHED-ITEM</div></div>复制代码
前几天在群里有老哥问:怎么在 Flex 布局中强制换行啊?
我心里一惊,Flex 不是这样用的。谨记:尽管有主轴和副轴之分,但 Flex 是一维布局。不过,强制换行也可以做到,有很多种方法,以下展示通过子元素设置 flex-basis: 100% 达到强制换行的示例:
<div class="b1" stype="display: flex; flex-wrap: wrap;"> <div class="bb p010 bg-gray tac" style="margin: 1px; border: 1px dashed; height: 50px; line-height: 50px;">DASHED-ITEM</div> <div style="flex-basis: 100%" /> <div class="bb p010 bg-gray tac" style="margin: 1px; border: 1px dashed; height: 50px; line-height: 50px;">DASHED-ITEM</div></div>复制代码
FFC 中稍微要注意的地方就是,子元素的 vertical-align、float、clear 属性会失效。
Line Height & Vertical Align,先挖坑,以后再跳。想要详细了解的话,可以先康康《CSS世界》,链接直达。
假使 CSS 中指定了页面运用的字体,浏览器要怎么查找与匹配它们呢?以下是详细步骤:
比如我把博客字体中所有弯引号的映射都改成了直角引号,见下图:
其实原理比较简单,直接康康定义的 CSS:
@font-face { font-family: 'Quote'; unicode-range: U+201c, U+201d, U+300C, U+300D;}@font-face { font-family: 'Noto Serif CJK';}:root { --font-text: 'Quote', 'Noto Serif CJK';}article { font-family: var(--font-text);}复制代码
也就是定义了一个只包含了引号 Unicode 映射的字体“Quote”,而中文字符或是英文字符在这个字体中找不到对应的映射,则回退至“Noto Serif CJK”中进行匹配。在这篇文章可以康康关于我使用直角引号这件事的起因《关于标点那些事儿》。
前阵子看了蛮多字体相关的东西,我会再写一篇《前端字体技术进阶》(挖坑),不过最近要开始准备找工作啦,得咕咕咕一阵子...
不使用 font-size: larger | smaller
慎用 font-size: <percent-value>
关于“如何在项目中管理CSS”的话题够写一本书了。这里简要介绍一下在我博客项目中采用的一种组织 CSS 的方法——ITCSS。
平常我们写 CSS 可能会碰到以下问题:
其实这一切都是 CSS 本身的特征导致的问题,CSS 本身是弱逻辑的,“装饰性”的,这注定了一般情况下我们不会重视它——没有文档、没有质量保证机制——所以写 CSS 时常常陷入“用新的样式去覆盖旧的样式”的怪圈(对,往 index.css 文件尾部添加一个带有 !important 声明的选择器)。而遵守 ITCSS 理论能够约束我们的行为,它是由 csswizardry 提倡的一种用来组织与管理项目中的样式文件的体系结构,一种元框架,或是一种 CSS 设计方法论。
ITCSS(Inverted Triangle CSS) 的名字很形象,这和它的核心概念有关,它通过规范样式文件的组织结构来适应项目中特殊性不断增加的选择器。见以下倒立的三角形,其中每一层都代表一种样式的概念结构:
实践理论将带来的好处显而易见:层级自上而下,选择器影响的 DOM 数量也越来越少,同时选择器特殊性递增。修改某个样式时我们可以轻易从相关组织文件中做出修改,而不影响其它样式,或是导致 CSS 样式继承的崩塌。
一个使用 ITCSS 组织的项目,其 index.css 可能长这个样子:
CSS 命名方案解决的主要是命名冲突和复用两个问题。这里简单总结几种较常用的,了解一下,方便以后直接选坑往里面跳。
CSS 居中往往是新手们抱怨的问题。下面,我们分别探讨水平居中、垂直居中和水平且垂直居中三种常见问题。
行内级元素(如 Inline,Inline Block)
单个块级元素(如 Block)
多个块级元素
单行行内级元素
多行行内级元素
块级元素
其实水平且垂直居中,无非就是上两种方案的整合版本。
总的来说,在不需要考虑兼容性的环境使用 Grid 布局,可以完美规避居中问题,因为使用 Grid 布局往往意味着你的网站页面排版已经定型。如果不能使用 Grid,那么局部使用 Flex 布局是非常省心的做法。
若需要考虑兼容性,那么用回 Margin 吧,Margin: Auto 会成为你的好助手。
使用 Line Clamp 属性
模拟截断
CSS 大小写敏感吗?
2秒内回答:Pixel(px)是相对长度单位还是绝对长度单位?
display: none 与 visibility: hidden 的区别
letter-spacing 与 word-spacing 的区别
vertical-align 默认对齐哪里?
我想所有人都知道要怎么预览页面的打印结果,Ctrl + P,等待一段时间,搞定,见下图:
不过如果页面复杂的话,就需要等待相当长的时间了... 可以从这几点开始进行优化:
@media print, speech { /* ************************************************************ */ /* *************************************************** 版面调整 */ .navbar, .sidebar-mask, .sidebar, .table-of-contents, #valine-vuepress-comment { display: none; } /* **************************************************************** */ /* *************************************************** 具体元素调整 */ .page > div.content__default { // ... }}复制代码
Rendering 选项卡还有很多好玩的东西,举个例子:
还有一些东西就不逐一介绍了,打开 控制台 -> More Tools -> Rendering 可以自己玩玩。
到别人的博客里,我经常会比划比划页面上的元素,看看它们的实现。
通过控制台的 Styles 选项卡能看到元素具体的属性及属性继承关系,不过比较麻烦,不够直观。有时我喜欢用浏览器自带的标尺:
不过,还是插件香——来看看 Visbug 插件的功能,虽然 Visbug 自身也有 Bug,但总比浏览器自带的强多了:
刚学习如何使用 CSS 时,我曾对 CSS 编码时的繁琐感到抵触,认为这是一项不值得一学的语言。这种反感保持了很久,直到近来我对 CSS 进一步了解后,反感才逐渐消散。当知道一门语言是怎么被设计的,身处什么样的时代,要解决什么问题,才能理解它独有的特征。
希望本文能对你有所帮助,如果文中出现了不流畅或理解错误的地方也麻烦各位评论指出。