# CSS 综合知识面试题答案解析
关于答案解析
- 有关 CSS 综合知识 相关的面试题答案解析过程是根据自身项目实践以及查阅官方文档等最终的得出结论。
- 仅供学习参考,评论区感谢补充和纠错 !
# 1、CSS 选择器权重和优先级(知乎、小米、字节、小红书)
答案解析
选择器的类型 | 实例 | 权值 | 等级 |
---|---|---|---|
id 选择器 | #id | 1000 | 第一等级 |
class、伪类、属性选择器 | .box 、:hover 、[type='text'] | 100 | 第二等级 |
标签选择器、伪元素选择器 | div 、::after | 10 | 第三等级 |
通配符、子选择器、相邻选择器等 | * 、> 、+ | 1 | 第四等级 |
继承的样式没有权值 | 0 |
# CSS 选择器的优先级比较规则
详细解读
上面我们把选择器分为了 4 个等级,那么选择器在比较时,也是按等级逐个来比较的。
- 第一层比较:找是否有行间样式 ,如果有以行间样式为主 ,如果没有再看选择器优先级
- 第二层比较:找第一等级选择器 ,个数多的权重最高,如果都没有,则看第二等级选择器。
- 第三层比较:第二等级选择器 ,个数多的权重最高,如果都没有,则看第三等级选择器。
- 第四层比较:第三等级选择器 ,个数多的权重最高,如果都没有,则看第四等级选择器。
- 第五层比较:第四等级选择器 ,个数多的权重最高,如果都没有,则看是否继承父元素样式。
如果在比较时,选择器权重优先级相同,那写在后面的样式会覆盖掉前面的样式
特别注意,易错点
- 选择器最终都是要选择到元素本身才可以,否则元素本身的样式都是继承过来,权重最低
点击查看源代码
<style>
#box #item1 .desc {
color: blue; /*span有自己的字体颜色,则不会继承父元素*/
font-size: 40px; /*span本身没有设置字体大小,则会继承这里的字体大小*/
}
span {
color: red; /*标签选择器样式大于继承样式,所以红色为主*/
}
</style>
<body>
<div class="box" id="box">
<div class="item1" id="item1">
<div class="desc">
<span>字体红色,字体大小是40px</span>
</div>
</div>
</div>
</body>
# 面试题:以下文字大小,颜色,背景色,边框分别多少 ?
点击查看源代码
<style>
#box #item1 .desc span {
color: blue; /*永远都是行间样式权重是高,所以字体以行间的红色为主*/
}
#box .desc {
/*这里并没有直接作用到span元素,所以span元素的字体大小是以继承得来*/
font-size: 20px;
}
span {
font-size: 40px; /*span最终呈现的字体大小是 40px*/
}
#item1 span {
/*因为#item1 id选择器大于class类选择器,则背景以黄色为主*/
background-color: yellow;
}
.box .item1 .desc span {
/*只要没有id选择器,再多的类选择器权重也不可能大于一个id选择器权重*/
background-color: skyblue;
}
.box .item1 span {
/*类选择器权重相同,但两个类会叠加,则权重大于下面一个类的*/
border: 2px solid red; /*最终边框为红色*/
}
.desc span {
border: 2px solid blue;
}
</style>
<body>
<div class="box" id="box">
<div class="item1" id="item1">
<p class="desc">
<span style="color:red">我的样式</span>
</p>
</div>
</div>
</body>
# !important 提升 css 优先级
TIP
!important
的作用是提升优先级。换句话说,加了这句的样式的优先级是最高的(比行间样式的优先级还高)
- 不过这种方式基本不用,不利于 css 样式的重写和 js 对样式的操作
点击查看源代码
<style>
span {
color: red !important; /*这里的优先级最高,高于行间样式*/
}
</style>
<body>
<span style="color:blue;">我的颜色是红色</span>
</body>
# 2、类选择器、伪类、伪元素的区别及优先级?(字节)
答案解析
类选择器用于给元素定义单独的样式
.box {
width: 100px;
height: 100px;
border: 1px solid red;
}
伪类是用于向某些选择器添加特殊的效果。
:hover
当鼠标滑到当前元素时,会做什么效果.box:hover{background-color: red;}
:first-child
选择父元素的首个子元素,添加相应.box:first-child{font-size: 20px;}
伪元素:也称为伪对象,它不存在于 DOM 文档中、是一个虚拟的元素。它可以用来代表某个元素的子元素,但是这个子元素并不存在于文档树中。他具有行内元素的特性,我们可以用 css 样式来修饰他。
常见的伪元素有:
::before
该伪元素定义在元素之前添加内容::after
该伪元素定义在元素之后添加内容::first-line
该伪元素向文本的首行添加特殊样式::first-letter
该伪元素向文本的第一个字母添加特殊样式
优先级:类选择器 = 伪类 > 伪元素
# 3、css 样式隔离,怎么解决样式隔离(知乎、京东)
答案解析
- CSS 一旦生效,就会应用于全局,所以很容易出现冲突,所以需要进行样式隔离。
- CSS 隔离是将 CSS 样式通过特殊方法安置在独立环境中,暂时避免和其他 CSS 污染。
CSS 样式隔离的方法有:
- 命名空间:给每个不同模块使用的 css 规划好命名,这样所有的 css 就都不会出现冲突,这种方法虽然很好理解和简单,但是编写起来很繁琐,维护成本会很高。
- CSS Modules:每一个 dom 都给了一个独立的 Hash,对于它们上面所挂在的 css 样式通过该 hash 绑定即可。Vue 就是这种方案,现在打包工具可以很轻松的就实现这种代码
- css-in-js 技术:CSS 全部变成内联样式,完全在 js 中写 css。 其中有一个很大的缺点,违背了 js css 分离原则,不能使用预处理器,编辑器不友好。
styled-components
应该是 css-in-js 最热门的一个库 - Shadow DOM: Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。
接下来重点讲解下 Shadow DOM 的用法
# Shadow DOM 基础概况
详细解读
Shadow DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中,它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。
这里,有一些 Shadow DOM 特有的术语需要我们了解:
- Shadow host:一个常规DOM 节点,Shadow DOM 会被附加到这个节点上。
- Shadow tree:Shadow DOM 内部的 DOM 树。
- Shadow boundary:Shadow DOM 结束的地方,也是常规 DOM 开始的地方。
- Shadow root:Shadow tree 的根节点。
你可以使用同样的方式来操作 Shadow DOM,就和操作常规 DOM 一样——例如添加子节点、设置属性,以及为节点添加自己的样式(例如通过 element.style
属性),或者为整个 Shadow DOM 添加样式(例如在 <style>
元素内添加样式)。
不同的是,Shadow DOM 内部的元素始终不会影响到它外部的元素(除了 :focus-within
),这为封装提供了便利。
# Shadow DOM 的基本用法
① 给常规 DOM 挂载 Shadow DOM
语法:
var shadowroot = element.attachShadow(shadowRootInit);
- element 表示 DOM 元素
- attachShadow() 方法,用来返回一个 ShadowRoot 对象
- shadowRootInit 用于指定 Shadow DOM 树封装模式的字符串,有两个值
- open 表示 shadow root 元素可以从 js 外部访问根节点
- closed 表示拒绝从 js 外部访问关闭 shadow root 节点
案例:给 box 盒子上挂载 Shadow DOM
点击查看源代码
<body>
<div id="box"></div>
<script>
var box = document.getElementById("box"); //获取box元素
var shadowRoot = box.attachShadow({ mode: "open" }); //在box元素上挂载shadow root
var p = document.createElement("p"); //创建元素p
p.innerHTML = "我是影子DOM"; //给元素添加内容
var styleElm = document.createElement("style"); //创建 style标签
//创建style样式
styleElm.textContent = `p{font-size:50px;color:red;}`;
shadowRoot.appendChild(styleElm); //style标签(样式)添加到shadow root上
shadowRoot.appendChild(p); //p标签添加到shadow root上
</script>
</body>
② 将 Shadow DOM 附加到 custom element(自定义元素)上
语法格式:
class 自定义元素名 extends HTMLElment {
constructor() {
//必须首先调用 super方法
super();
//元素的具体功能写在下面....
//创建shadow root
//var shadowRoot=this.attachShadow({mode:'open'});
}
}
将 Shadow DOM 挂载到自定义标签 my-custom
点击查看源代码
<body>
<my-custom imgsrc="images/1.jpg" title="倾听你的声音"
>我是p标签中内容</my-custom
>
<script>
class myCustom extends HTMLElement {
constructor() {
super();
//创建shadow root
var shadowRoot = this.attachShadow({ mode: "open" });
var img = document.createElement("img");
img.setAttribute("class", "img");
var h3 = document.createElement("h3");
h3.setAttribute("class", "title");
var p = document.createElement("p");
p.setAttribute("class", "info");
//p标签中内容
p.innerHTML = this.innerHTML;
//标题内容
var title = "";
if (this.hasAttribute("title")) {
title = this.getAttribute("title");
}
h3.innerHTML = title;
//图片地址需要从外部传入,所以需要先判断 myCustom标签上是否有传入图片地址的属性
var imgUrl;
if (this.hasAttribute("imgsrc")) {
imgUrl = this.getAttribute("imgsrc");
} else {
imgUrl = "images/default.jpg";
}
img.src = imgUrl;
//创建 style样式
var style = document.createElement("style");
style.textContent = `
/*:host 是控制自定义标签样式*/
:host{
padding:10px;
border:5px solid #ddd;
display:inline-block;
text-align:center;
}
.img{
width:150px;
height:150px;
}
.title{
margin:0;
padding:0;
font-size:20px;
line-height:40px;
color:#000;
}
.info{
margin:0;
font-size:16px;
color:#666;
line-height:25px;
color:#666;
}
`;
//将所创建的元素添加到Shadow DOM上
shadowRoot.appendChild(style);
shadowRoot.appendChild(img);
shadowRoot.appendChild(h3);
shadowRoot.appendChild(p);
}
}
//定义新的元素
customElements.define("my-custom", myCustom);
</script>
</body>
# 8、浏览器如何确定哪些元素与 CSS 选择器匹配 ?
答案解析
浏览器从最右边到左边匹配选择器
- 浏览器根据选择器过滤掉 DOM 中的元素,并向上遍历其父元素以确定匹配项。
- 选择器链的长度越短,浏览器可以越快地确定该元素是否与选择器匹配。
例如:
- 使用这个选择器
p span
,浏览器首先找到所有<span>
元素,然后一直向上遍历其父元素直到根以找到<p>
元素。 - 对于特定的
<span>
,一旦找到<p>
,它就知道<span>
匹配并可以停止匹配。
# 9、CSS 在浏览器底层是如何工作的 ?
答案解析
浏览器的整个渲染过程
- 解析 HTML,构建 DOM 树
- 解析 CSS,生成 CSS 规则树
- 合并 DOM 树和 CSS 规则树,生成 render(渲染)树。
- 布局 render 树(回流 / 重排),负责各元素尺寸、位置的计算。
- 绘制 render 树(painting 重绘),绘制页面像素信息
- 浏览器会将各层的信息发送给 GPU,GPU 会将各层合成(composite),显示在屏幕上。
构建渲染树时,浏览器主要完成以下工作
- 从 DOM 树的根节点开始遍历每个可见节点
- 对每个可见节点,找到 CSS 规则树中对应的规则,并应用它们
- 根据每个可见节点以及其对应的样式,组合生成渲染树
不可见节点(也就是不会出现在渲染树中的节点)
- 一些不会渲染输出的节点(如:script、meta、link 等)
- 一些通过 css 进行隐藏的节点(如:
display: none
)
不过要注意:visiblity 和 opacity 隐藏的节点是会显示在渲染树上的。
# 10、为什么我们更推荐外部 CSS 或 Javascript 而不是内联 ?
答案解析
内联 CSS 或 Javascript 对网站性能有不良影响
- 当您使用内联脚本时,你的 HTML 代码会变得更重,而外部脚本会减小 HTML 文件大小,这有助于快速呈现网页。
HTML 代码永远不会被缓存,所以内联脚本与此相反,外部依赖项
- 例如 CSS 和 JavaScript 文件,将由访问者的 Web 浏览器缓存。
- 因此,它减少了每次用户点击网页时的 https 请求。
很难维护内联 CSS 和 Javascript 代码 ,将代码仅放在一个集中位置比更改分布在网站上所有文件中的完全相同类型的代码片段要好得多。
# 11、CSS 有什么缺点 ?
答案解析
- 浏览器兼容性:支持某些样式选择器,有些不支持。我们必须使用@support 选择器确定支持或不支持的样式。
- 跨浏览器问题:某些选择器在不同浏览器中的行为不同。
- 没有父选择器:目前,使用 CSS,你无法选择父标签。
# 12、Reset 与 Normalize CSS 的区别?它们有何不同 ?
答案解析
Reset CSS:CSS 重置旨在删除所有内置的浏览器样式。例如:所有元素的边距、填充、字体大小都重置为相同。
Normalize CSS:规范化 CSS 旨在使内置浏览器样式在浏览器之间保持一致,它还纠正了常见浏览器依赖项的错误。
# 13、CSS 优化、提高性能的方法有哪些 ?
答案解析
(1)尽量将样式写在单独的 css 文件里面,在 head 元素中引用 将代码写成单独的 css 文件有几点好处:
- 内容和样式分离,易于管理和维护
- 减少页面体积
- css 文件可以被缓存、重用,维护成本降低
(2)不使用 @import
(3)避免使用复杂的选择器,层级越少越好,建议选择器的嵌套最好不要超过三层。比如:精简页面的样式文件,去掉不用的样式
(4)利用 CSS 继承减少代码量
(5)避免!important
,可以选择其他选择器
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X