什么是 CSS?

前言

做为程序员,技术的落实与巩固是必要的,所以想到写个系列,名为 why what or how 每篇文章试图解释清楚一个问题。css

why what or how 的第二章,什么是 CSShtml

释义

CSS - Cascading Style Sheets,层叠样式表程序员

CSS 也算是一种标记语言,其内容做为浏览器的输入,浏览器会解析其文本内容,做为 HTML 附加的样式信息,用以修饰标签。bootstrap

标记语言,一类以固定的形式描述文档结构或是数据处理细节的语言,通常为纯文本形式,其内容做为其余程序的输入。数组

样式表你们都很清楚,记录样式信息的表格,以键值对的形式存在,格式以下浏览器

p {
    font-size: 10px;
}
复制代码

那么何为层叠呢?层叠的意义是什么?sass

层叠,字面意思:层层叠加,咱们知道 HTML 的页面结构是树状的,不一样标签的层级嵌套最终组合造成了页面,那么从另外一个角度来看,页面的结构就是一层一层的,以下图所示bash

html-tree

html 为最底层,body 位于 html 层之上,headernavarticleasidefooter 这些标签位于 body 之上,section 位于 article 之上,标签的堆叠就造成了网页结构,那么这关 CSS 什么事?less

先抛出一个问题:若是说 article 标签表明一篇文章,那么其内部的文字大小,样式,排版是否是应该一致?ide

是!那么样式进行层叠的意义也在于此,只要我给 article 标签固定的样式,那么 article 内部的标签就会(部分)继承这个默认的样式信息,层级结构表明了节点间的关系,那么就有了父子级的区别,也就有了继承的关系。

那有的朋友就会问了?如今的 CSS 不就是这样的吗?这不是默认的行为?

CSS 确实如此,但刚开始为 HTML 进行修饰的样式规则却并不是如此,CSS 只是在众多规则中,最终成为标准的那一个。

历史

  1. 1991 - 1993 年,各类浏览器相继出现,但每一个都实现了本身制定样式规则,网页呈现由用户控制,样式没有统一的规定。
  2. 1993 年,Mosaic 浏览器采用增长新种类的 HTML 标签实现样式的表达,以知足设计师的要求,<FONT></FONT> 之类表明样式的标签开始出现。
  3. 1994 年,Håkon W Lie 提出层叠 HTML 样式表(Cascading HTML Style Sheets),CSS 的雏形出现。
  4. 1995 年,W3C 创建,W3CCSS 很感兴趣,为此专门组织了一次讨论会。
  5. 1996 年,CSS 语法完成,发布了 CSS1.0 ,但因为当时主流的浏览器并不支持(每家都有本身的样式写法),而且当时主流的方式为 HTML 的样式标签,所以 CSS 等几年后才流行起来。
  6. 1997 年,W3C 组织了专门管 CSS 的工做组。
  7. 1998 年,浏览器市场份额被微软公司的 IE4 和网景公司的 Netscape Navigator 两大浏览器巨头所占据。IE4 率先实现了 CSS 1.0 但因为不重视,致使规范实现不完善,bug 不少。因为不一样浏览器支持的标准不一致,网页设计人员不得不为 IENetscape Navigator 分别设计一套网页。民间设计人员团体 网页标准计划(WaSP) 发动水军将 W3C 的建议宣扬为标准,并批评还未加盟 W3C 标准的业界厂商。
  8. 1998 年,W3C 组织出版 CSS2.0 网页标准计划的 7 位成员成立了 CSS武士团(CSS Samurai),指出 Opera 浏览器和 IE 浏览器在支持 CSS 方面存在的诸多问题。Opera 公司着手解决了问题,但微软并未解决。他们同时也劝说其余浏览器开始支持 CSS 标准。
  9. 1999 年,W3C 开始制定 CSS3 标准。
  10. 2003 年,Dave Shea 推出了一个名为 CSS禅意花园("CSS Zen Garden") 的站点,向人们展现出仅经过应用不一样页面样式规则,就能够实现对网页艺术风格的面目一新。
  11. 2006 - 2009年, DIV+CSS 布局逐步取代缺少灵活性的传统表格布局,无表格网页设计成为网页内容布局的主流方案。
  12. 2009 - 至今CSS3 标准已部分公布,但仍未所有制订完毕,浏览器厂商也逐步跟进,W3C 官方将这些不一样的特性分门别类,称为 modules,不在沿用 CSS3.0 的版本号,而是将单独的 module 分别命名,每一个 module 也有不一样的版本号。
  13. ...

CSS 从标准制定,到最终有浏览器开始实现,至少间隔了 5 年的时间,而 CSS 的上位也由民间组织推进,到浏览器厂商最终采纳。直到目前,咱们还能在 HTML 中使用相似 <FONT></FONT> 之类表明样式的标签,也是历史遗留下来的产物,但 HTML5 的推出,规范了 HTML 标签是做为网页的结构,CSS 做为网页的样式信息,所以那些表明样式的标签已经被规范所移除,咱们也应该少用甚至不用。

CSS 在众多的样式规则中脱颖而出,其实和 IE4 的成功有这很大的关系,IE 系列最早实现了 CSS1.0 标准,能够说是在与 Netscape Navigator 战争中胜利的因素之一,甚至不少的 CSS3 属性背后也有着 IE 的影子,但后来 IE 系列的落寞很大程度上是由于它的不做为。

语法 or 结构

一条 CSS 规则的结构以下:

┌─────── 选择规则 ──────┐   ┌────────── 声明块 ──────────┐
p + p > span:first-child    {
                                ┌──────  CSS 属性 ───────┐
                                font-size   : 10px       ;
                                └─ 属性名 ─┘ └─ 属性值 ─┘
                            }
复制代码

由两部分组成,选择器与声明块。

  • 选择规则:用于匹配具体 HTML 中符合要求的标签
  • 声明块:用于设置符合要求标签的样式

一条选择规则有两部分组成,选择器与链接符,上面例子中,pspan:first-child 属于选择器,而 +> 这些符号属于链接符。

一个声明块由多条 CSS 属性组成,属性分为两部分,属性名与属性值,以 : 分隔,以 ; 结尾,一条属性规定了标签的一个样式。

选择规则

选择规则,意如其名,用于选择 HMTL 文档中的标签,那么如何进行选择的呢?选择规则分为两部分,咱们分开介绍

  • 选择器

选择器用于选择 HMTL 页面中存在的标签。选择器分为几大类,以下:

选择器类型 含义
元素选择器(elementname 选择对应标签
类选择器(.classname 选取具备对应类名的标签
ID 选择器(#idname 选取具备对应 ID 的标签
通配选择器(* 选取全部标签
属性选择器([属性名=值] 选取有相应规则属性的标签
伪类选择器 选取伪类规定的标签

属性选择器有多种写法,以下:

写法 含义
[attr] 选取带 attr 属性的标签
[attr=value] 选取 attr 属性值为 value 的标签
[attr~=value] 选取 attr 属性中有 value 单词(单词不与其余字母相连)存在的标签
[attr|=value] 选取 attr 属性为 value 或以 value- 开头的标签
[attr^=value] 选取 attr 属性以 value 开头的标签
[attr$=value] 选取 attr 属性以 value 结尾的标签
[attr*=value] 选取 attr 属性中含有 value 的标签

注: 属性选择器写法,如 [attr=value] 其后均可以跟 i,好比 [attr=value i] 表明在匹配时,忽略 value 的大小写。

经常使用伪类,以下

写法 含义
:active 选中被用户激活的标签
:hover 选中被鼠标悬浮的标签
:visited 选中已访问过的连接
:focus 选中获取到焦点的标签
:first-child 选中一组兄弟标签中的第一个标签
:last-child 选中一组兄弟标签中的最后一个标签
:first-of-type 选中一组兄弟标签中其类型的第一个标签
:last-of-type 选中一组兄弟标签中其类型的最后一个标签
:not(X) 选中不被 X 选择器选中的全部标签
:nth-child(an+b) a b为固定值,n 为任意天然数,选中一组兄弟标签中第 an+b 个元素
:nth-last-child(an+b) 同上规则,从后往前匹配
:nth-of-type(an+b) 同上规则,从前日后找,匹配相同类型的标签
:nth-last-of-type(an+b) 同上规则,从后往前匹配
:only-child 若是父标签中只有一个子元素则选中该子标签
:only-of-type 若是父标签中只有一个该类型的子元素则选中该子标签
  • 链接符(符号用引号引发)

链接符规定了选择器该如何进行组合,为了方便解释,我会将符号取一个名字,最终的选择规则表明的含义只需按顺序从前日后读便可。

符号 名字 含义
''(无) 而且 选择器叠加
',' 或者 选择器共用
' '(空格) 后代选择
'>' 内第一级 子元素选择
'+' 以后的第一个 相邻兄弟选择
'~' 以后的全部 兄弟选择

给几个例子

  1. p.class1
  2. p,div
  3. p .class1
  4. p > .class1
  5. p + .class1
  6. p ~ .class1

咱们顺起来念:

  1. 选中 p 而且带有 class1 类名的标签。
  2. 选中 p 标签或者 div 标签。
  3. 选中 p 标签内的带有 class 类名的标签。
  4. 选中 p 标签内第一级带有 class 类名的标签。
  5. 选中 p 标签以后的第一个带有 class 类名的标签。
  6. 选中 p 标签以后的全部带有 class 类名的标签。

针对于复杂的选择规则的编写,好比在 bootstrap 中有这样一段:

.btn-group > .btn-group:not(:first-child):not(:last-of-type) > .btn {
  border-radius: 0;
}
复制代码

表明的意义是什么呢?

按照顺序念:选中带有 btn-group 类名标签内第一级带有 btn-group 类名而且不是第一个子元素而且不是最后一个子元素标签内第一级带有 btn 类名的标签。

对应的 html 以下:

<span class="btn-group">
    <span class="btn">按钮1</span>
    <span class="btn-group">
        <span class="btn">按钮2</span>
        <span class="btn">按钮3</span>
        <span class="btn">按钮4</span>
    </span>
    <span class="btn">按钮5</span>
</span>
复制代码

选中的标签为按钮2/3/4。

咱们从该条规则的意义来理解这条规则:btn-group 中的 btn-group 下的 btn 不该该有圆角(在中间时)。由于正常的按钮都是带圆角的,而放在按钮组中的按钮其实只要左右两边的按钮带上圆角就好,这时候就须要经过特殊的手段来吧这些要去除圆角的元素给选择,并去掉圆角。

接着咱们在看咱们翻译出来的内容,是否是不那么绕了呢?

最后在提一段 bootstrap 中的样式规则,你们一块儿翻译翻译吧

.btn-group > .btn-group:last-of-type:not(:first-child) > .btn:first-child {
  border-bottom-left-radius: 0;
  border-top-left-radius: 0;
}
复制代码

选择器的内容到此为止,接下来就要谈谈声明块中的 CSS 属性了。

样式类别(属性)

CSS 中样式分不少种,按照样式效果进行区别,大体能够分为以下几类

大类 做用 表明属性
字体 控制字体的显示效果 font-* color text-transform text-decoration text-shadow
文字排版 控制文字的排版 text-align text-align-last text-indent text-overflow line-height word-spacing letter-spacing
背景 控制元素背景显示 background-*
布局 控制元素的布局行为 flex 系列属性 grid 系列属性
文档流相关 控制元素在文档流中的位置 position top left bottom right z-index float clear
列表 控制列表的行为 list-*
盒模型 控制元素大小 width height padding border margin box-sizing
动画 & 过渡 控制元素动画 transition-* transform animation-* @keyframes

该篇仅介绍 CSS 是什么,而不解释 CSS 有什么,因此不过于深究这些属性的具体内容,能够经过查看 CSS参考来了解。

属性的继承

开篇咱们就提到了,CSS 为层叠样式表,层叠表明的意思为属性的继承。

这个继承能够简单的总结为一句话:

父级标签的字体样式和文章排版样式会被子标签所继承,也就是说子标签不用写这些属性,就拥有了这些属性。

几个特殊值

CSS 中有几个特殊的属性值,须要特别关注一下:

  1. unset - 若是该属性为继承属性则使用继承值,不是则使用浏览器默认值
  2. initial - 使用初始化的值,也就是浏览器默认值
  3. inherit - 使用继承值

权重

因为相同的标签能够由不一样的选择规则所选中,那么这时候就出现了一个状况,若是有多个选择规则同时选中了同一个元素,而且同时设置了相同属性,那么标签最终是要按照哪条规则定义的属性来显示?

不一样 CSS 规则对同一个标签设置了相同属性,CSS 选择规则权重最高的规则会覆盖权重低的样式设置。

何为权重,通过上面的介绍,CSS 设置标签的样式有以下几种

  • 直接写在标签 style 属性上
  • ID 选择器
  • 类名选择器
  • 伪类
  • 标签选择器
  • 通配符

从上到下,选择器的权重依次递减,就像是一样是一张纸币,100 的纸币表明的比 10 块的纸币大,可是与纸币不一样的是,权重高的永远比权重低的优先级要高,无论低权重的选择器有几个。

除去 style 的方式咱们用一个数组来表明选择规则的权重。

[0, 0, 0, 0, 0]
复制代码

从左到右为:ID 选择器类名选择器伪类标签选择器通配符,数组的初始值全为 0 ,咱们能够变看选择规则来肯定最终数组,好比那个很复杂的 btn 选择:

.btn-group > .btn-group:not(:first-child):not(:last-of-type) > .btn {
  border-radius: 0;
}
复制代码

从左到右,每出现一个选择器,就将对应的数组内的数组加一,那么上条规则最终的数组以下:

[0, 3, 2, 0, 0]
复制代码

这时,有的朋友可能会问了,伪类选择有 4 个啊,怎么是 2 ?这里须要注意的是::not 伪类仅带有取反的意思,并不增长权重。

那么权重该如何比较?

简单的来讲,从前日后比,谁比谁先大,谁的权重高。相信写一段比较程序你们就了解了:

function compare(weight1, weight2){
    for(let i = 0; i < 5; i++){
        if(weight1[i] !== weight2[i]){
            if(weight1[i] > weight2[i]){
                console.log('参一权重大');
            }else{
                console.log('参二权重大');
            }
            return;
        }
        
    }
}
复制代码

那么 style 所规定的样式权重如何?

style 样式所规定的权重,比 ID 选择器 的权重还要高。

那么咱们想要在 CSS 文件中修改 style 所规定的样式该怎么办?

使用 !important 修饰特定的样便可。以下所示

p {
    color: red !important;
}
复制代码

那么 p 标签的字体颜色即为红色,即便设置了 style 也没用。

总结一下:权重等级由高到低为:!important > style > 选择规则。就像是老大哥说东小弟不敢说西,权重也是绝对服从上一级的。

所以为了不出现尴尬的情况,请慎重使用 !important

预编译

CSS 发展到如今,人们发现 CSS 虽然含有层叠的含义,可是写法倒是一维的,就好比文档结构以下的一个网页:

<article>
    <p>这是一段文字</p>
</article>
复制代码

咱们要在 articlep 标签上设置样式,看起来是这样的:

article {
    font-size: 14px;
}

article p {
    color: red;
}
复制代码

因为样式规则按照一条一条的形式进行编写,看起开就是一维,标签的层级结构不能在其中体现,若是这样写那就行了:

article {
    font-size: 14px;
    p {
        color: red;
    }
}
复制代码

从样式编写上就说明了层级结构,p 标签所继承的样式明了,可是浏览器又仅能识别一维的样式编写,那该如何让浏览器认识该结构呢?

预编译器因运而生,经常使用的 css 预编译器有:sasslessstylus等,这些预编译器须要特定的语法,但都支持二维的写法。

那么这些个预编译干了什么?

很简单的一句话,将符合预编译器语法的文件转化为 css 文件。

一样的这些预编译器的语法,这里不过多介绍,提供几个网站供你们查阅:

CSS Modules 的见解

我是在 2019 年写下这篇文章,为什么要特意的声明时间,是由于一个东西的出现:CSS Modules,咱们须要好好来了解了解。

何为 CSS Modules ,官方解释以下:

CSS files in which all class names and animation names are scoped locally by default.

翻译过来就是:

CSS 的类名和动画名字都在一个命名空间下。

不懂?写个例子。

如下为伪源码:

// test.css
.box{
    color:red;
}
复制代码
import style from 'test.css'

function Test(){
    return (<div class={style.box} />)
}
复制代码

如下为伪输出:

<div class="_styles__box_34682763478"></div>
复制代码

_styles__box_34682763478 就是在加了命名空间后的类名,这样构建出的代码就不会出现相同类名被覆盖的问题。

总的来讲,CSS Modules 作了一件时,混淆了 class 类名、id 和动画名。

这时就有一些开发站出来讲,这个东西好啊,我不再用去想类名该如何取才能不致使冲突了。冷静下来先考虑这几个问题:

  1. 虽然不会冲突了,但若是你想覆盖类名该怎么办?
  2. 是否能接受编译慢的问题?
  3. 样式名会随着文件的位置或文件内容改变,是否能接受这种变化?
  4. 页面上是否须要用到大量 JavaScript DOM API?若是是,那么经过样式名选取变的不可靠。
  5. 当使用了 CSS Modules 后项目中是否出现了极多的 :global 若是是,那要仔细思考下,不用 CSS Modules 成本是否更低?

如下是问题对应的一些场景

问题一:你引用了团队中其余人写的组件,但须要你本身修改(覆盖)一些样式,而其余人也是用 CSS Modules 编写样式,所以他的类名是不固定的,你该如何去覆盖这个组件中标签的样式?

  • 通知组件编写者修改样式?要是通用组件咋办?
  • 让他用固定样式?那用 CSS Modules 的意义在于哪?

问题三四:当你程序须要使用 DOM API 但经过 CSS Modules 生成的样式名会随着文件的位置或内容改变,这样程序就变得不可靠,固然这个问题有办法解决

  • js 中也用 CSS Modules 的命名,只不过调试变得些许困难
  • js 中使用的样式名与 CSS Modules 进行区分,jsCSS Modules 使用两套样式单独的样式

那么接下来讲说个人使用体验,或者说我以为好的使用体验

  1. 项目中 CSS Modules 和原始的 CSS 一同使用。
  2. CSS Modules 仅使用在组件内部,项目中依然使用公共样式。
  3. CSS Modules 仅使用在一些与结构无关的但又很差命名的标签上,这些标签通常也不会被 js 所选择。
  4. CSS Modules 混淆采用和文件位置相关的命名空间产生方式,而不根据文件内容。
  5. 一旦某个样式是跨组件通用就不用 CSS Modules ,经过项目规定的命名空间在原始 CSS 文件中定义。
  6. 开发 UI 库或公共组件,不用 CSS Modules

总的来讲就是一个原则,CSS Modules 用在非共用,无所谓命名以及非跨组件通用的标签上,这些标签能够认为是组件的内部状态,不会被外部影响或修改。

也不知道多年之后 CSS Modules 是否真的解决了编译慢的问题(至少个人电脑上是),CSS Modules 原理上还是传统的 CSS 编写方式,只不过它混淆了名称,并添加了映射,可是之后的发展会如何呢?我不知道,但我会持续的关注它。

总结

惯例以问句开篇,用问句来结尾

  1. CSS 的层叠体如今哪?
  2. CSS 选择规则分为那两部分,每部分都如何组成?
  3. 列一列选择器,并分个类?
  4. 常见属性都有哪些?
  5. 预编译器是干什么用的?
  6. CSS Modules 是什么?
  7. 谈谈对 CSS Modules 的见解吧。

最后,其实本篇还想谈谈布局和文档流的内容,但篇幅过长,也涉及到了 HTML ,因此就打算将布局单独出来,之后会有如何进行网页布局的单篇,已经记录,但愿你们持续关注。

参考

最后的最后

该系列全部问题由 minimo 提出,爱你哟~~~