1. CSS@规则
@charset
@import
@media
@page:打印机相关
@counter-style
@keyframes:https://www.w3.org/TR/css-animations-1/
@fontface:https://www.w3.org/TR/css-fonts-3/
@supports:不推荐使用该规则检测浏览器css兼容性
@namespace:
2. CSS规则的结构
- Selector
- Declaration
- Key
- Properties
- Variables: https://www.w3.org/TR/css-variables/
- Value
- https://www.w3.org/TR/css-values-4/
- 可能类型: cal、number、number with units
- Key
3. CSS选择器
3.1 选择器语法
- 简单选择器
*
div
类型选择器,svg中有一个a标签和html中的a标签重复,注意使用@namespace区分.cls
类选择器#id
id选择器[attr=value]
属性选择器:hover
伪类选择器::before
伪元素选择器
- 复合选择器
- <简单选择器 ><简单选择器><简单选择器 >
*
或者tagName
必须写在前边
- 复杂选择器
- <复合选择器><复合选择器>,子孙选择器
- <复合选择器>">"<复合选择器>,父子选择器
- <复合选择器>"~"<复合选择器>,兄弟选择器,
p~ul
选择前面有<p>
元素的每个<ul>
元素- <复合选择器>"+"<复合选择器>,兄弟选择器,
div+p
选择紧接在<div>
元素之后的所有<p>
元素- <复合选择器>"||"<复合选择器>,leve4新增,选中表格某一列
3.2 选择器的优先级
- 行内样式
<div style="xxx"></div>
==> 标记a类- ID 选择器 ==> 标记b类
- 类,属性选择器和伪类选择器 ==> 标记c类
- 标签选择器、伪元素 ==> 标记d类
- 计算方式
[a,b,c,d] ==> a*N^ + b*N^2 + c*N + d
,其中N
在大多数浏览器中取值为65536
3.3 伪类
- 链接/行为
:any-link
匹配所有超链接:link
匹配未访问的超链接,:visited
匹配已访问的超链接,这两个伪类只能修改元素的字体颜色:hover
:active
:focus
:target
- 树结构
:empty
匹配没有子元素的元素:nth-child()
:nth-last-child()
:first-child
、:last-child
、only-child
- 除了empty之外的伪类会增加CSS的计算难度,可能会影响浏览器样式渲染的性能
- 逻辑型
:not
:where
、:has
,leve4新增
3.4 伪元素
::before
::after
,设置content
属性后就可以生成盒,像其他元素一样参与后续的布局排版::first-line
,选择第一行::first-letter
,选中第一个字母
4 一个小练习
//元素和选择器是否匹配
function match(selector, element) {
let selectorParts = selector.split(' ').reverse();
let match = /(?<tagName>(\w+)?)(?<id>(#\w+)?)(?<classNames>(.[\w.]+)?)/;
//取到第一个选择器,该选择器必须和当前节点匹配
let part = selectorParts.shift();
let matchResult = part.match(match);
let {tagName, id, classNames} = matchResult.groups;
let _tagName = element.tagName.toLowerCase();
let _classList = element.className.split(' ');
let _id = element.id;
if(id && id !== ('#'+_id) || tagName && tagName !== _tagName) {
return false;
}
if(classNames){
let classList = classNames.split('\.').filter(val => !!val);
if(_classList.length < classList.length) {
return false;
}else {
for (let className of classList) {
if (_classList.indexOf(className) === -1) {
return false;
}
}
}
}
//走到此处说明已匹配目标元素
let len = selectorParts.length, i = 0;
element = element.parentElement;
for(let part of selectorParts) {
let matchResult = part.match(match);
let {tagName, id, classNames} = matchResult.groups;
element: while (element){
let _tagName = element.tagName.toLowerCase();
let _classList = element.className.split(' ');
let _id = element.id;
if(id && id !== ('#'+_id) || tagName && tagName !== _tagName) {
element = element.parent;
continue;
}
if(classNames){
let classList = classNames.split('.').filter(val => !!val);
if(_classList.length < classList.length) {
element = element.parent;
}else {
for (let className of classList) {
if (_classList.indexOf(className) === -1) {
element = element.parent;
continue element;
}
}
}
}
//走到此处证明匹配到了,则该跳出循环匹配下一个元素和选择器了
element = element.parentElement;
i++;
break;
}
//判断元素是否为空,为空表示遍历到了根节点
if(!element) {
break;
}
}
//i !== len说明选择器未遍历完,但是已经遍历到了根节点
return i === len;
}
match("div #id.class", document.getElementById("id"));