CSS 基础类题目
content-box:标准盒模型,width只包含content
border-box:怪异盒模型,width包含content、border和padding
有的时候我们在使用padding时候会导致容器被左右撑开,这时候修改下box-sizing就行了。
CSS选择器
Section titled “CSS选择器”CSS的选择器可以分如下几类:
-
基础选择器:最常用的核心选择器。
-
元素选择器:如
p{},选中所有<p>标签。 -
类选择器 (Class):如
.nav{},选中所有class="nav"的元素。 -
ID选择器:如
#header{},选中id="header"的元素。 -
通配选择器:如
*{},选中页面所有元素。
-
-
组合选择器:用于表达元素间的特定关系。
-
后代选择器 (空格):如
div p{},选中<div>内部所有的<p>。 -
子选择器 (>): 如
ul > li{},只选中作为<ul>直接子元素的<li>。 -
相邻兄弟选择器 (+):如
h2 + p{},选中紧跟在<h2>后面的第一个<p>。 -
通用兄弟选择器 (~):如
h2 ~ p{},选中跟<h2>同级的所有后续<p>。
-
-
属性选择器:通过元素的属性及属性值来匹配元素。
-
[attr]:选中带有attr属性的元素。 -
[attr="value"]:选中属性值等于value的元素。 -
[attr^="value"]:选中属性值以value开头的元素。
-
-
伪类与伪元素选择器:用于选择元素的特定状态或虚拟元素。
-
伪类 (
:): 定义元素的特殊状态,如:hover(鼠标悬停)、:focus(获得焦点)、:first-child(第一个子元素)。 -
伪元素 (
::):用于创建并样式化不在文档树中的元素,如::before、::after用于创建生成内容,::first-line样式化首行文本。
-
CSS优先级的计算基于选择器的特异性(Specificity),它决定当多个规则作用于同一元素时,哪个规则会生效。优先级由四个等级(a, b, c, d)的数值来表示,比较时从左到右(从a到d)逐级比较,级高者胜出,而不是看总和。
其计算公式为:(a, b, c, d)
-
内联样式 (a = 1):写在元素 style 属性中的样式,优先级最高。
例如:
<p style="color: red;">→ 特异性为 (1, 0, 0, 0) -
ID选择器 (b):每使用一个ID选择器,b 值+1。
例如:
#header→ 特异性为 (0, 1, 0, 0) -
类、伪类、属性选择器 (c):类选择器 (如 .container)、类选择器 (如
:hover,:focus)、属性选择器 (如[type="text"]),每使用一个,c 值+1。 -
元素选择器、伪元素选择器 (d):元素(标签)选择器 (如 div, p)、伪元素选择器 (如
::before,::after),每使用一个,d 值+1。
可以参考下面示例的计算结果:
| 选择器示例 | 特异性计算值 | 以 (a,b,c,d) 表示 |
|---|---|---|
style="..." | 1,0,0,0 | (1,0,0,0) |
#main .content p | 0,1,2,1 | (0,1,2,1) |
ul#nav li.active a | 0,1,1,3 | (0,1,1,3) |
h1 + p::first-letter | 0,0,0,3 | (0,0,0,3) |
li:hover | 0,0,1,1 | (0,0,1,1) |
div | 0,0,0,1 | (0,0,0,1) |
比较时从左到右比较,值大者胜,如:
-
(0,1,0,0) 高于 (0,0,2,3) (因为 b=1 > b=0)
-
(0,1,1,0) 高于 (0,1,0,5) (因为 c=1 > c=0)
例外:在样式声明后加上!important会覆盖任何其他声明(包括内联样式),拥有最高优先级。
z-index 与 层叠上下文
Section titled “z-index 与 层叠上下文”层叠上下文的定义可参考MDN - 层叠上下文。
层叠上下文的计算往往是根据父元素来的,即在同一个父元素中进行z-index的比较,值较大者在上层。这种比较不会跨层级进行。
重排 - Reflow
Section titled “重排 - Reflow”当渲染树中的一部分因为元素的规模、尺寸、布局、隐藏等改变而需要重新构建的过程,称为重排。这相当于浏览器需要重新计算所有元素的位置和几何信息。
重排的成本非常高,因为它是一种阻塞行为。浏览器需要从当前重排的元素开始,递归地重新计算所有子节点和后续同级节点的尺寸和位置,然后更新渲染树。这个过程会占用主线程,导致后续的JS执行和渲染被延迟,如果频繁发生,会造成页面卡顿、掉帧。
会引发重排的一些场景:
-
页面首次渲染。
-
添加或删除可见的DOM元素。
-
元素尺寸、位置、内容发生改变(如
width,height,padding,margin,left,top等)。 -
浏览器窗口大小改变(
resize事件)。 -
激活CSS伪类(如
:hover可能导致布局变化)。 -
JavaScript频繁地读取和修改DOM样式,引发布局抖动
重绘 - Repaint
Section titled “重绘 - Repaint”当元素发生的改变只影响其外观风格(如颜色、背景色、边框颜色、可见性等),而不影响其布局时,浏览器不需要重新计算几何属性,只需要根据元素的新样式重新绘制它到屏幕上,这个过程称为重绘。
重绘跳过了布局计算和树构建阶段,直接进入绘制阶段。相较于重排,重绘的开销要小得多。
引发重绘的一些场景:修改color, background-color, border-color, visibility等样式。
布局抖动问题
Section titled “布局抖动问题”布局抖动是指浏览器在短时间内被迫进行多次连续的布局(Layout) 或重排(Reflow) 操作。这是一种性能问题,会导致页面卡顿、响应缓慢,严重影响用户体验。
浏览器有渲染队列机制,连续的读/写操作会被合并。但如果在一个写操作后立即进行一个读操作(如读取offsetTop, scrollHeight, getComputedStyle等),为了得到精确的值,浏览器会立即强制清空队列并进行重排。
下面是一个反例:
// 一个非常糟糕的、会导致严重布局抖动的例子function resizeAllParagraphsToMatchBlockWidth() { // 获取所有段落元素 const paragraphs = document.querySelectorAll('p');
// 可怕的“读写循环” for (let i = 0; i < paragraphs.length; i++) { // 1. 【读】获取元素的宽度 (offsetWidth) // 浏览器:“哦!你要读布局信息,我必须先计算一下当前的布局才能给你准确值!” // -> 触发一次强制同步布局! const currentWidth = paragraphs[i].offsetWidth;
// 2. 【写】修改元素的宽度 (会影响布局) // 这个修改并不会立即触发布局,浏览器本可以稍后批量处理。 paragraphs[i].style.width = currentWidth + 'px'; } // 循环结束后,浏览器可能还会再进行一次布局来计算所有修改后的结果。}在上面的例子中,每一次循环都先读 (offsetWidth),然后立即写 (style.width)。下一次循环的读操作,迫使浏览器必须处理上一次写操作造成的布局变更,以确保数据的准确性。这就导致了 N 次(元素数量)强制同步布局,性能急剧下降。
为了规避上述问题,在开发过程中需要注意:避免在修改布局之后又立即去读取布局信息,遵循“批量读写”的原则。优化后的代码如下:
function resizeAllParagraphsToMatchBlockWidth() { const paragraphs = document.querySelectorAll('p');
// 第一阶段:批量【读】 const allWidths = []; for (let i = 0; i < paragraphs.length; i++) { // 一次性读取所有需要的布局信息 allWidths.push(paragraphs[i].offsetWidth); }
// 第二阶段:批量【写】 for (let i = 0; i < paragraphs.length; i++) { // 使用第一阶段读取的数据进行写入 paragraphs[i].style.width = allWidths[i] + 'px'; } // 现在总共只触发了 2 次布局(1次用于批量读,1次用于处理批量写),而不是 N 次。}元素隐藏的几种方式以及区别
Section titled “元素隐藏的几种方式以及区别”当我们要隐藏一个元素时,可以采用如下方式:
-
display: none:不占空间,完全消失,触发重绘和重排 -
opacity: 0:本质就是变透明,依旧占空间 -
visibility: hidden:占空间,但是在页面上不可见,仅触发重绘顺带一提,当设置元素 visibility: collapse 后,一般的元素的表现与 visibility: hidden 一样,也即其会占用空间。但如果该元素是与 table 相关的元素,其表现却跟 display: none 一样,也即其占用的空间会释放。
flex 布局
Section titled “flex 布局”flex 布局的常用属性
Section titled “flex 布局的常用属性”父容器:
-
display: flex -
justify-content、justify-items、align-items、align-content -
排列方向:
flex-direction -
换行:
flex-wrap
子容器:
-
flex-shrink、flex-basis、flex-grow、flex -
排列顺序:
order -
align-self