本篇文章已授权微信公众号 dasu_Android(大苏)独家发布
声明
本系列文章内容全部梳理自以下四个来源:
- 《HTML5权威指南》
- 《JavaScript权威指南》
- MDN web docs
- Github:smyhvae/web
作为一个前端小白,入门跟着这四个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。
正文-CSS基础
1.结构和语法
首先需要清楚,CSS 职责是控制 HTEM 文档里的文本内容在网页上样式呈现的效果,写的每一个样式最终是通过选择器作用到具体的元素上面。
工作原理
互联网其实都是通过网页相互连接组成的,每个网页都是一份 HTML 文档,因此浏览器与后端的首次通信是以 HTML 文档为基准,那么整个流程的第一步也就是解析 HTML 文档。
如果在解析 HTML 文档过程中发现有通过 <link> 标签引用了外部的 CSS 文件时,那么浏览器会去下载相对应的 CSS 文件。
当 HTML 文档解析完毕后会生成一个 DOM 文档结构,DOM 文档结构中记录着每个节点的元素,各元素之间的关系,有点类似于 Android 中的 View 树。
最后,通过 CSS 的选择器将相对应的样式作用到 DOM 中选择器找到的元素节点,然后浏览器渲染呈现在网页上。
结构
以上是一个 CSS 的典型结构,总共由两部分组成:选择器 + 样式
{} 大括号内部的都是具体的某种样式,可用来控制元素的背景、大小、排版位置等样式效果。而 {} 左边的则是选择器,用来指定说后面跟随的样式列表要作用到 HTML 文档中的哪个元素上。
选择器类型很多,规则也很多,因此会有一种现象,就是某个元素被不止一个选择器匹配到,如果某个样式属性起冲突了,那么就会按照一定的优先级顺序处理。
样式属性也很多,具体也后面介绍,但有一点需要先明确一下,如果使用了未知的样式属性,或者给某个样式属性赋予了无效值,那么该样式属性会被视为无效,浏览器的 CSS 引擎会完全忽略它。
盒模型
跟 Android 很类似,每个元素在页面上都是占据一个矩形区域,也分 margin 和 padding,唯一不同的就是这里的模型多了个 border,虽然在 Android 中也有 border 的概念,但它是在 padding 区域内的,不单独占据某块区域,所以这里需要区分一下。
因此,一个元素在页面上的宽高就是由四个部分共同影响的了:content 区域,padding 区域,border 区域,margin 区域。
需要注意的是,上图中的盒子模式的 box-sizing 属性为默认值 content-box 模式。
这种模式下,width 和 height 指的只是content区域的宽高。
box-sizing 属性的取值有:content-box, padding-box, border-box, margin-box。四种,对应的就是指明 width 和 height 表示的是包含哪些区域的宽高。
使用方式
CSS 基本结构是由选择器和样式属性列表组成,那么如何跟 HTML 文档关联起来使用呢?一共有三种方式:
- 内嵌样式
使用 HTML 元素的全局属性 style 声明,仅影响一个元素,除非工作环境受限,比如只允许编辑 HTML 的 body,否则强烈不推荐这种方式。
示例:
<a href="index.html" style="background: gray; color: #6a90d9">点击跳转</a>
- 内部样式表
在 HTML 中的 <head> 内使用 <style> 标签,在某些情况下很有用,比如当你不能直接修改 CSS 文件时。
示例:
<style type="text/css">
a {
background: gray;
color: #6a90d9;
}
</style>
- 外部样式表
将 CSS 保存在一个独立的扩展名为 .css 的文件中,并在 HTML 的 <head> 里使用 <link> 元素中引用它,这种方法可以说是最好的,因为你可以使用一个样式表来设置多个文档的样式,并且需要更新 CSS 的时候只要在一个地方更新。
<link type="text/css" rel="stylesheet" href="css/nms.css">
2.选择器
选择器,顾名思义,就是将 css 代码选择到 HTML 文档中具体的元素对象,并作用在它身上。
基本选择器
基本选择器其实是一些比较常用、简单的选择器,包括:元素选择器、id 选择器、class 选择器、属性选择器、组合选择器。这些在第一篇前端入门1-基础概念中已经介绍过了,这里不就详细说了。
基本选择器规则很简单,选择器基本就是一两个条件,满足了即可匹配上,如 a.class,p#id 等,即使有稍微经过组合,但仍旧不复杂,但有些应用场景下需要通过复杂的规则,即需要满足多个条件下才能匹配上。
属性选择器
在第一篇中介绍的属性选择器其实是最基本用法的一种,而它还支持其他很多规则的用法,如下:
[attr] | 选择定义attr属性的元素,不管属性的取值具体是什么 |
---|---|
[attr=”val”] | 选择定义attr属性,且属性值为val的元素 |
[attr^=”val”] | 选择定义attr属性,且属性值以字符串val打头的元素 |
[attr$=”val”] | 选择定义attr属性,且属性值以字符串val结尾的元素 |
[attr*=”val”] | 选择定义attr属性,且属性值包含字符串val的元素 |
[attr~=”val”] | 选择定义attr属性,且属性值具有多个值,其中一个为字符串val的元素。 |
[attr|=”val”] | 选择定义attr属性,且属性值为连字符(-)分割的多个值,其中第一个为字符串val的元素。 |
也就是说,属性选择器不仅可以用来匹配那些具有指定属性的元素,还可以进一步根据不同属性值来匹配。
并集选择器
结构:<选择器>, <选择器>
并集选择器是通过 ,
逗号将不同选择器组合使用的一种选择器,这种情况下,各个选择器之间是没有任何关系,都是相互独立的,就是他们具有相同的样式属性表而已。
这只是一种简便写法的用法而已,具有相同样式属性表的不同再复制粘贴,可以直接通过 ,逗号将不同选择器分开即可。只有 HTML 文档中的元素满足其中一个选择器即可。
示例:
a, h1, span, div {
background-color: black;
}
上述示例中有四个选择器含有同样的样式属性,HTML 文档中只要满足其中一个选择器即可被匹配到。
后代选择器
结构:<第一个选择器> <第二个选择器>
多个选择器之间通过空格
分隔开的话表示这是一个后代选择器,也就是说,需要先满足第一个选择器的前提下,在第一个选择器匹配到的元素的后代元素中去匹配第二个选择器。
这里的后代包括了子孙后代。
示例:
p span {
background-color: black;
}
<span>第一个span</span>
<p>第二个<span>span</span></p>
以上示例中,选择器要匹配的元素是位于 p 元素的后代元素中的 span 元素,因此第一个 span 元素就不符合规则,而第二个 span 则会被匹配到。
儿子选择器
结构:<第一个选择器> > <第二个选择器>
儿子选择器是多个选择器之间通过 >
右箭头符号连接,表示的是在满足第一个选择器的前提下,从它匹配到的元素的直接子元素中寻找第二个选择器。
跟后代选择器的区别就在于它只能在直接子元素中匹配第二个选择器。
示例:
p > span {
background-color: black;
}
<span>第一个span</span>
<p>第二个<span>span</span></p>
<p><div>第三个<span>span</span></div></p>
第一个 span 元素不是 p 元素的后代,第二个 span 元素是 p 元素的直接子元素,第三个 span 元素是 p 元素的孙子元素,因此只有第二个 span 元素满足规则被匹配到。
兄弟选择器
结构:<第一个选择器> + <第二个选择器>
兄弟选择器是多个选择器之间通过 +
加号连接。表示的是,在满足第一个选择器的前提下,从它匹配到的元素的紧跟着的位于同一层级的下一个元素,看该元素是否符合第二个选择器。
也就是说,兄弟选择器,两个选择器所匹配的元素要求,位于同一层级,且相邻。
示例:
p + a {
background-color: #6a90d9;
}
<a>第一个a</a>
<p>第二个<a>a</a></p>
<a>第三个a</a>
<a>第四个a</a>
上述示例中,同时满足位于同一层级,且相邻,且需要先满足第一个选择器的前提下,还满足第二个选择器这四个条件的 a 元素就是第三个 a 元素了。
普通兄弟选择器
结构:<第一个选择器> ~ <第二个选择器>
普通兄弟选择器,是多个选择器之间通过 ~
波浪符号连接。表示的是满足第一个选择器的前提下,从它匹配到的元素后,去寻找位于同一层级,且在该元素后面的所有满足第二个选择器的元素。
兄弟选择器只匹配相邻的一个元素,而普通兄弟选择器则是可以匹配位于元素后面的所有符合第二个选择器的元素。
示例:
p ~ a {
background-color: #6a90d9;
}
<a>第一个a</a>
<p>第二个<a>a</a></p>
<a>第三个a</a>
<a>第四个a</a>
同时满足同层级,且在 p 元素后面的兄弟元素有两个,第三个 a 元素和第四个 a 元素,因此这里可以匹配到这两个元素。
伪选择器
选择器的目的就是为了匹配到 HTML 文档中的满足条件的元素,然后将样式属性作用在元素上。
元素是什么,在基础一节中有介绍过,元素其实就是包含了标签以及文本内容的整块内容。因此被选择器匹配到的元素,都是直接将 CSS 样式作用到整个元素上,也就是作用到整个文本内容上。
那么,如果有一些需求并不是直接去匹配 HTML 文档中的具体元素,而是指定了一些状态、行为,然后让浏览器动态去根据当前情况选择符合这些状态、行为的元素。
或者有一些需求是并不想将 CSS 样式作用到整个元素上,而是只作用到元素标记的文本内容的某一部分。
这个时候,这种选择器就称作伪选择器,因为它有区别于普通选择器的行为
伪选择器总共分成两种:伪元素选择器,伪类选择器
伪元素选择器
当伪选择器最终将 CSS 作用的对象并不是整个元素,而是满足条件的元素标记的文本内容的某一部分时,称伪元素选择器。
伪元素选择器不多,如下:
::first-line | 匹配满足条件的元素标记的文本内容的首行部分 |
---|---|
::first-letter | 匹配满足条件的元素标记的文本内容的首字母部分 |
:before | 在满足条件的元素之前插入生成的内容 |
:after | 在满足条件的元素之后插入生成的内容 |
伪元素选择器的用法基本都是和其他选择器组合使用,比如 p::first-line 表示匹配 p 元素标记的文本内容的首行部分。
而 :before 和 :after 与之前的选择器都不大一样,因为之前介绍的选择器作用都只是用于匹配选择 HTML 文档中的元素或文本内容而已。但这两个伪元素选择器会生成内容,并插入到匹配到元素的文本内容中去。
因此,它们的基本用法通常都是这样:
a:before {
content: "在文本内容之前插入";
}
a:after {
content: "在文本内容之后插入";
}
有一种应用场景很适合使用这两种伪元素选择器,当需要对列表动态的生成复杂的编号规则时,可以结合 :before 和 counter() 使用。在 CSS 中也是可以使用一些内置的方法功能。
伪类选择器
当不是通过 HTML 文档中元素的一些基本性质,比如 id,class,标签名,属性这些基本特征来匹配这些元素时,就可以称作伪类选择器。
伪类选择器是通过满足一些指定状态、行为下来匹配元素的一种选择器,比如满足是否获取焦点等等。
伪类选择器相对来说,比较多,如下:
:first-child | 选择元素的第一个子元素 |
---|---|
:last-child | 选择元素的最后一个子元素 |
:only-child | 选取元素的唯一一个子元素 |
:only-of-type | 选取属于父元素的特定类型的唯一子元素 |
:nth-child(n) | 选取元素的第n个子元素 |
:nth-last-child(n) | 选取元素的倒数第n个子元素 |
:nth-of-type(n) | 选取属于父元素的特定类型的第n个子元素 |
:nth-last-of-type(n) | 选取属于父元素的特定类型的倒数第n个子元素 |
:enabled | 选取启用状态的元素 |
:disable | 选取被禁用状态的元素 |
:checked | 选取所有选中的复选框和单选按钮元素 |
:default | 选取默认的元素 |
:valid :invalid | 选取基于输入验证判定的有效或者无效的input元素 |
:in-range :out-of-range | 选取被限定在指定范围之内或之外的input元素 |
:required :optional | 根据是否允许使用required属性选取input元素 |
:link | 选取未访问的链接元素 |
:visited | 选取用户已访问的链接元素 |
:hover | 选取鼠标指针悬停的元素 |
:active | 选取当前被用户激活的元素,这通常意味着用户即将点击该元素 |
:focus | 选取获得焦点的元素 |
:not(<选择器>) | 否定选择,(如选择所有不匹配<选择器>的元素) |
:empty | 选取不包含任何子元素或文本的元素 |
:lang(<语言>) | 选取lang属性为指定值的元素 |
:target | 选取URL片段标识符指向的元素 |
一些伪类选择器看下说明应该就清楚怎么使用,不明白的再具体去查找相关文档即可。
3.层叠算法
由于一个元素通常会被多个选择器命中,而这些选择器又有可能是通过不同方式作用到元素上,因此这里存在了两种场景下的优先级问题,但请记住,只有当作用到同一个元素上的样式属性起了冲突时才会存在优先级问题。
如果不同选择器作用到同一个元素上,但它们各自的样式属性列表中没有重复的,那就不存在冲突,也就不存在优先级问题,都会一起合并作用到元素上。
场景1:不同使用方式的优先级
CSS 有三种使用方式,另外浏览器也有默认样式,因此这些构成了一个优先级顺序:
- 元素内嵌样式(全局属性 style 定义的样式)
- 文档内嵌样式(style 标签定义的样式) 和 外部样式(link 标签引入的外部 CSS 文件)
- 浏览器中的用户样式
- 浏览器中的默认样式
以上优先级从高到低,同层级之间,如果存在冲突的样式属性的话,以文档中最后出现的属性为准,采用覆盖规则。
场景2:不同选择器之间的优先级
当作用到同一个元素上的不同选择器存在样式属性冲突时,优先以场景1考虑优先级,如果不存在场景1的情况,即起冲突的选择器在场景1中处于同一层优先级,那么才会考虑不同选择器之间的优先级。
- id 选择器
- class 选择器,属性选择器,伪类选择器
- 元素选择器,伪元素选择器
以上优先级从高到低,同层级之间,如果存在冲突的样式属性的话,以文档中最后出现的属性为准,采用覆盖规则。
但,有时候,使用的是组合选择器,那么这时候就需要依靠一定的算法来计算出谁的优先级高了,这个算法叫做层叠算法:
通过对以上不同选择器赋予某个数值来计算整个组合选择器的最终数值,然后比较大小。
比如,上面三个优先级的选择器中,1优先级的表示100,2优先级的表示10,3优先级的表示1,以此来计算一个组合选择器的数值大小。
通常来说,组合选择器不会过于离谱,长达十几个选择器的组合,因此以上述赋予每个优先级的数值足够覆盖绝大多数场景。
示例:
h1,和 #indentifier 都只是简单选择器,不通过这种层叠算法计算也行。
h1 + p::first-letter 和 li > a[href*=”zh-CN”] > .inline-warning 这种比较复杂的组合选择器,就可以根据赋予每一层级优先级对应的数值来进行计算,最终根据数值大小比较谁的优先级高。
这种赋予不同优先级某个具体数值具现化的思想叫做层叠算法,通常是用于比较复杂的组合选择器时。
但实际开发中,很少会需要用到层叠算法,掌握场景1和场景2下简单的优先级分辨理论基础足够了。实际开发过程中,没必要这么复杂,借助开发工具或者运行查看下效果就可以确认谁的优先级高低了。
大家好,我是 dasu,欢迎关注我的公众号(dasuAndroidTv),如果你觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~