重温前端三件套(二):CSS
基础
为什么要初始化CSS样式
消除浏览器之间的差异,提高兼容性提高代码质量
第一点:
未初始化时,当我们添加进去一个DIV,会发现他并不是紧贴着窗口的,而是有一定的距离的。这就是一个例子,在不同的浏览器中,对一些标签是具有的默认值的,而且不同的浏览器默认值也肯是不一样的。如果没有对其 CSS样式做初始化,就可能导致在不同的浏览器之间展示的效果是不一样的。
第二点:
初始化后,便于我们对代码的统一管理,减少重复样式等。同时修改的时候便于统一管理。
| 方案 | 特点 | 评价 |
|---|---|---|
1. 暴力法 (* {}) |
用通配符 * 强制清零。 |
不推荐。性能差,且会把输入框、按钮改得很难看。 |
2. Reset流 (reset.css) |
把所有标签样式“归零”(h1变小,ul去点)。 | 适合强定制。像一张白纸,一切自己重写。 |
3. Normalize流(normalize.css) |
现代主流。保留有用的默认值,只修复 Bug。 | 强烈推荐。“和而不同”,是 Vue/React 脚手架的默认选择。 |
HTML页面中 id 和 class 有什么区别
- 应加”#“,class选择符前缀应加”.”
- id属性在一个页面中书写时只能使用一次,而class可以反复使用
- id作为元素标签用于区分不同结构和内容,而class作为一个样式,可以应用到任何结构和内容当中去
- 布局上的一般原则:id先确定结构和内容再为它定义样式。而class正好相反,是先定义样式,然后在页面中根据不同需求把样式应用到不同结构和内容上
- 目前浏览器都允许同一个页面出现多个相同属性值的id,一般情况能正常显示,不过当javascript通过id来控制元素时就会出错
- 在实际应用中,class常被用到文字版块和页面修饰上,而id多被用在宏伟布局和设计包含块,或包含框的样式。
伪元素和伪类
(:)用于伪类,(::)用于伪元素。
::before以一个子元素存在,定义的一个伪元素,只存在页面中。
伪元素:对选择元素的指定部分进行修改样式,常见的有 :before,:after,:first-line,first-letter 等等
伪类:对选择元素的特殊状态进行修改样式,常见的有 :hover,:active,:checked,:focus,:first-child 等等
| 特性 | 伪类 (Pseudo-classes) | 伪元素 (Pseudo-elements) |
|---|---|---|
| 符号 | : (单冒号) |
:: (双冒号) |
| 核心逻辑 | 状态/位置 (State/Structure) | 创造/部分 (Creation/Part) |
| DOM 影响 | 选中现有的完整元素 | 创建虚拟元素或选中元素内容的一部分 |
| 典型例子 | :hover, :focus, :nth-child(2) |
::before, ::after, ::placeholder |
| 一句话记忆 | “当它变成…样子的时候” | “在它里面造一个…” 或 “选中它的…” |
单位
px % em 这几个单位,可以适用于大部分项目开发,且拥有较好兼容性
- px : 一个固定像素单位,一个像素表示终端屏幕能显示的最小的区域
- % :元素的宽高可随浏览器的变化而变化,实现响应式,一般子元素的百分比相对于直接父元素
- em : 作为 font-size 的单位时,代表父元素的字体大小按比例计算值,作为其他属性单位时,代表相对自身字体大小按比例计算值
1 | |
- rem : CSS3新增。相对于根元素字体大小按比例计算值;作用于根元素字体大小时,相对于其出初始字体大小(16px)
1 | |
- vw 相对于视图窗口宽度,视窗宽度为100vw
- vh 相对于视图窗口高度,视窗高度为100vh
- rpx 即 **responsive pixel(动态像素)**是微信小程序独有的,解决屏幕自适应的尺寸单位,可以根据屏幕宽度进行自适应,无论屏幕大小,规定屏幕宽度为750rpx,通过rpx设置元素和字体大小,可实现小程序在不同尺寸的屏幕上自动适配
| 单位 | 类型 | 参考对象 (基准) | 记忆口诀 | 推荐场景 |
|---|---|---|---|---|
| px | 绝对 | 屏幕物理像素 | 死板砖头 | 边框、最小字号、阴影 |
| % | 相对 | 直接父元素 | 拼爹 | 栅格布局、流式布局 |
| em | 相对 | 父元素(字体) 或 自己(宽高等) | 变色龙 | 首行缩进 (text-indent: 2em) |
| rem | 相对 | HTML根元素 | 中央集权 | 移动端H5适配 (主流) |
| vw/vh | 视口 | 浏览器窗口大小 | 看窗外 | 全屏背景、首屏海报 |
| rpx | 适配 | 屏幕宽度 (等分750份) | 切蛋糕 | 微信小程序专用 |
block,inline,inline-block
块级元素:自动占据一行,可以设置宽高
常见的有 div,p,h1-h6,ul,li,form,table
行内元素:占据一行的一小部分,多个行内元素水平排版,无法设置宽高
常见的有 span ,img,a
行内块级元素:跟行内元素类似,不过可以设置宽高
常见的有 button ,img , input, select, label,textarea
空元素:img,br,input,link,meta
link标签的伪类和用法
1 | |
- a:link 未设置超链接则无效
- a:visited 针对URL,只要浏览器历史记录里有这个网址,所有指向它的链接都会变色。
- 遵循LVHA顺序
- focus是针对键盘用户,tab键作用时
重排、重绘和合成
回流reflow,也叫重排 layout。渲染树中部分或全部元素的尺寸、结构或属性变化,浏览器会重新渲染部分或全部文档。**回流一定触发重绘,重绘不一定触发回流。重绘开销小,回流代价高。修改样式的时候,**最好避免使用下面列出的属性,他们都会刷新渲染队列。**如果要使用它们,最好将值缓存起来。**触发回流的操作:
- 初次渲染
- 窗口大小改变(resize事件)
- 元素属性、尺寸、位置、内容改变
- 元素字体大小变化
- 添加或者删除可见 dom 元素
- 激活 CSS 伪类(如 :hover)
- 查询某些属性或调用某些方法
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
渲染好后重排的三种情况:
- 只读不改:浏览器通常有缓存
1 | |
- 只改不读:触发重排,浏览器标记为脏
1 | |
- 先改后读:触发重排,触发强制同步布局
1 | |
- vue中如果你不是直接操作 DOM (
style.width),而是修改数据 (this.width = 500),框架通常是异步更新 DOM 的:
1 | |
1 | |
- React呢?
重绘 repaint,某些元素的样式如颜色改变,但不影响其在文档流中的位置,浏览器会对元素重新绘制,不再执行布局阶段,直接进入绘制阶段
合成,利用transform、transition、opacity和filter可实现合成效果,即GPU加速。避开布局 分块和绘制阶段
一些优化点:
- 使用 visibility替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
- 不要使用 table 布局,可能很小的一个小改动会造成整个 table 重新布局
- 频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video 标签,浏览器会自动将该节点变为图层
法则一:批量化 (减少浏览器干活的次数)
浏览器很懒,别频繁骚扰它,攒一波一起做。
1. 样式集中修改
场景:你要通过 JS 给一个元素改宽度、改高度、还改背景。
❌ 错误写法 (触发 3 次重排/重绘)
1
2
3
4
5
6
7const el = document.getElementById('box');
// 浏览器:改宽了?我重排一下。
el.style.width = '100px';
// 浏览器:哎呀,高也改了?我再重排一下。
el.style.height = '100px';
// 浏览器:背景变了?我重绘一下。
el.style.backgroundColor = 'red';✅ 正确写法 (触发 1 次)
1
2
3
4
5// 方法 A:切换类名 (推荐)
el.classList.add('active-box');
// 方法 B:使用 cssText
el.style.cssText = 'width: 100px; height: 100px; background-color: red;';
2. DOM 批量操作 (documentFragment)
场景:你要往 <ul> 里插入 1000 个 <li>。
❌ 错误写法 (触发 1000 次重排)
1
2
3
4
5
6
7const list = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.innerHTML = i;
// 每次 append,列表高度都变了,导致页面布局变化
list.appendChild(li);
}✅ 正确写法 (触发 1 次)
1
2
3
4
5
6
7
8
9
10
11
12
13const list = document.getElementById('list');
// 创建一个“隐形托盘”
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.innerHTML = i;
// 先放到托盘上 (内存操作,不涉及渲染)
fragment.appendChild(li);
}
// 最后一次性端上去
list.appendChild(fragment);
法则二:隔离化 (缩小受影响的范围)
如果一定要动,就让它在一个小圈子里动,别影响整个页面。
3. 动画元素脱离文档流 (Absolute/Fixed)
场景:做一个小球在屏幕上乱跳的动画。
❌ 错误写法 (灾难级重排)
使用
margin-left或padding来推这个球。后果:球一动,它旁边的文字、下面的图片全都要重新计算位置。整个文档都在跟着球“抖动”。
✅ 正确写法 (局部重排)
设置
position: absolute或fixed。后果:球变成了浮在空中的图层。它怎么跳,底下的文字都纹丝不动。重排只发生在球这个小图层里。
4. 离线操作 DOM (display: none)
场景:你要对一个复杂的表格进行大规模的数据更新(改文字、改样式、加图标)。
❌ 错误写法 (连续重排)
- 直接在可见的表格上循环修改 100 个单元格。
✅ 正确写法 (两次重排)
1
2
3
4
5
6
7
8
9
10
11
12const table = document.getElementById('data-table');
// 1. 先把表格藏起来 (触发 1 次重排)
table.style.display = 'none';
// 2. 疯狂修改,怎么改浏览器都看不见 (触发 0 次)
updateTableData();
addIcons();
changeColors();
// 3. 改完再显示出来 (触发 1 次重排)
table.style.display = 'block';
法则三:硬件化 (利用 GPU 加速)
能用显卡解决的,别麻烦 CPU。
5. 移动位置 (Transform vs Left/Top)
场景:做一个侧边栏滑出的动画。
❌ 错误写法 (CPU 忙碌)
1
2
3
4
5
6
7
8.sidebar {
position: absolute;
left: -300px;
transition: left 0.5s; /* 每一帧都在改变几何属性,CPU 狂算布局 */
}
.sidebar.open {
left: 0;
}✅ 正确写法 (GPU 摸鱼)
1
2
3
4
5
6
7
8.sidebar {
/* 使用 transform,浏览器会把它提升为独立图层 */
transform: translateX(-100%);
transition: transform 0.5s; /* GPU 直接平移图层,不触发重排重绘 */
}
.sidebar.open {
transform: translateX(0);
}
6. 提前告知浏览器 (will-change)
场景:某个元素即将进行复杂的 3D 旋转。
✅ 优化写法
1
2
3
4.cube {
/* 告诉浏览器:一会儿我要变 transform 了,你先帮我把 GPU 图层建好 */
will-change: transform;
}
法则四:机制化 (遵守浏览器的规矩)
了解浏览器的工作原理,不给它添堵。
7. 读写分离 (避免强制同步布局)
场景:这是最容易踩的坑,在循环中读属性。
❌ 错误写法 (读->改->读->改)
1
2
3
4
5
6
7const divs = document.getElementsByTagName('div');
for (let i = 0; i < divs.length; i++) {
// 读:为了拿 offsetWidth,浏览器被迫强制重排上一轮的修改
const width = divs[i].offsetWidth;
// 写:标记为脏
divs[i].style.width = (width + 10) + 'px';
}✅ 正确写法 (读完再写)
1
2
3
4
5
6
7
8const divs = document.getElementsByTagName('div');
// 先全部读出来 (或者只读第一个,如果大家一样宽)
const currentWidth = divs[0].offsetWidth;
// 再批量写入
for (let i = 0; i < divs.length; i++) {
divs[i].style.width = (currentWidth + 10) + 'px';
}
8. 选择器优化 (从右向左)
场景:给导航栏里的链接加样式。
❌ 低效写法 (范围太广)
1
2/* 浏览器逻辑:先找页面所有的 a,再看它爸是不是 div,再看爷爷是不是 .nav */
.nav div a { color: red; }✅ 高效写法 (精准匹配)
1
2/* 浏览器逻辑:直接找类名为 .nav-link 的元素 */
.nav-link { color: red; }
9. 动画帧控制 (requestAnimationFrame)
场景:JS 实现一个进度条动画。
❌ 错误写法 (setInterval)
1
2
3
4
5
6// 假如浏览器正忙,或者屏幕刷新率是 60Hz (16.6ms),这里强行 10ms 执行一次
// 会导致丢帧、卡顿
setInterval(() => {
width += 1;
el.style.width = width + '%';
}, 10);✅ 正确写法 (rAF)
1
2
3
4
5
6
7
8
9
10
11
12function step() {
width += 1;
el.style.width = width + '%';
if (width < 100) {
// 浏览器:好的,等我准备好画下一帧的时候叫你
// 因为时循环的,每一帧变一次,所以这里在step内部也调用了requestAnimationFrame
requestAnimationFrame(step);
}
}
// 调用,把step函数这个变量交给浏览器在下一帧渲染时执行
requestAnimationFrame(step);
| 目的 | 核心手段 | 记忆口诀 |
|---|---|---|
| 减次数 | class 替换 style, fragment 替换 append |
打包批发,拒绝零售 |
| 减范围 | absolute 脱离流, display: none 离线改 |
圈地自萌,闭门造车 |
| 用 GPU | transform 替换 left/top |
外包显卡,不做苦力 |
| 防强制 | 读写分离,变量缓存 | 先看后买,别边看边买 |
盒子模型
1、盒模型宽度的计算
(1)普通盒模型
1 | |
width只包含内容content宽度,不包含border和padding
- offsetWidth = width + padding + border(不包括margin)
- inherit 指定 box-sizing 属性的值,应该从父元素继承
1 | |
(2)怪异盒模型(一般采用)
1 | |
offsetWidth = width(padding 和 border 都挤压到内容里面)box-sizing: border-box 的真实作用范围是: width = 内容 (content) + 内边距 (padding) + 边框 (border)。
2、margin 纵向重叠
margin 纵向重叠取重叠区最大值,不进行叠加。当两个垂直方向的块级元素相遇时,它们的间距不是相加,而是融合。
- 规则:两个相邻的
margin-top和margin-bottom相遇,最终间距 = max(margin1,margin2)。 - 注意:这只发生在垂直方向,水平方向(left/right)是不会重叠的,会老老实实相加。
- 解决:如果不想要这种重叠,可以给父元素设置 BFC(见下文)。
3、margin 负值问题:位移与“吸星大法”
Top & Left (影响自身):
margin-top: -20px;:把自己往上拽 20px。margin-left: -20px;:把自己往左拽 20px。
Bottom & Right (影响他人):
margin-bottom: -20px;:重点! 元素本身不动,但它告诉下方的元素:“我比实际上矮了 20px”。结果就是下方的元素会向上跳 20px,覆盖在当前元素上。margin-right: -20px;:同理,右侧元素会向左移。
4、BFC(Block Format Context:块级格式化上下文)
特征:
- 内部封闭性:BFC 内部的元素在布局上不会影响到外面的元素。
- 垂直排列:内部的盒子(Box)会在垂直方向一个接一个地放置。
- Margin 重叠限制:同一个 BFC 下的两个相邻盒子的垂直
margin会发生重叠(正如你之前看到的第 2 点)。但如果两个盒子分属于不同的 BFC,它们的margin就不会重叠。 - 清除浮动:计算 BFC 的高度时,内部的浮动元素也会参与计算(这就是为什么它可以“清除浮动”),防止高度塌陷(如果一个父元素内部全是float最后高度为0没有背景色)。
- 不与浮动盒子重叠:BFC 的区域不会与浮动(float)元素重叠。
形成 BFC 的条件:
- float 不设置成 none
- position 是 absolute 或者 fixed
- overflow 不是 visible (auto/scroll等)
- display 是 flex 或者 inline-block 等
5、float
(1)圣杯布局和双飞翼布局
作用:
实现 pc 端三栏布局,中间一栏最先渲染
实现两边宽度固定,中间自适应
(2)圣杯布局
略
(3)双飞翼布局
1 | |
(4)对比
| 属性 | 圣杯布局 | 双飞翼布局 |
|---|---|---|
| HTML | 包裹三栏 | 只包裹中间一栏 |
| 是否定位 | 相对定位 | 无需定位 |
| 左右栏的空间 | 使用 padding 预留 | 使用 margin 预留 |
| 左栏处理 | positon + margin-left | margin-left |
| 右栏处理 | margin-right | margin-left |
(5)手写clearfix
不触发BFC清除浮动,因为overflow:hidden会把超出的元素裁剪
1 | |
6、元素居中
(1)行内元素水平垂直居中
设置父级标签。
水平居中: text-align: center
垂直居中: line-height:盒子高度
(2)块级元素水平垂直居中
1 | |
7、样式单位
em:相对于自身字体大小的单位
rem:相对于 html 标签字体大小的单位
vh:相对于视口高度大小的单位,20vh == 视口高度/10020
vw:相对于视口宽度大小的单位, 20vw == 视口宽度/10020
margin 合并
margin 合并也叫外边框塌陷或者外边距重叠,注意不同于高度塌陷。
外边距重叠:块的上外边距(margin-top)和下外边距(margin-bottom)有时合并(折叠)为单个边距,其大小为单个边距的最大值(或如果它们相等,则仅为其中一个),这种行为称为边距折叠。
1、同一层相邻元素之间
<p>1 </p> <p>2 </p> <style> p:nth-child(1){ margin-bottom: 13px; } p:nth-child(2){ margin-top: 12px; } </style>
这里我们希望的是这两个之间的距离是 25px,但是实际上他们的距离是13px
2、没有内容将父元素和后代元素分开
<style type="text/css"> section { margin-top: 13px; margin-bottom: 87px; } header { margin-top: 87px; } footer { margin-bottom: 13px; } </style> <section> <header>上边界重叠 87</header> <main></main> <footer>下边界重叠 87 不能再高了</footer> </section>
3、空的块级元素
<style> p { margin: 0; } div { margin-top: 13px; margin-bottom: 87px; } </style> <p>上边界范围是 87 ...</p> <div></div> <p>... 上边界范围是 87</p>
margin和padding 的值为百分比时
当 padding 属性值为百分比的时候,如果父元素有宽度,相对于父元素宽度,如果没有,找其父辈元素的宽度,均没设宽度时,相对于屏幕的宽度。
不管 margin-top/margin-bottom 还是 margin-left/margin-right(对于padding 同样)也是,参考的都是 width。
那么为什么是 width, 而不是 height 呢?
CSS权威指南中的解释:
我们认为,正常流中的大多数元素都会足够高以包含其后代元素(包括外边距),如果一个元素的上下外边距时父元素的 height 的百分数,就可能导致一个无限循环,父元素的 height 会增加,以适应后代元素上下外边距的增加,而相应的,上下外边距因为父元素 height 的增加也会增加,如此循环。
BFC
块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
- BFC是一个独立的布局环境,与外部互不影响,用于决定块级盒的布局及浮动相互影响范围的一个区域。
- 块盒和行盒(行盒由一行中所有的内联元素所组成)都会垂直沿着其父元素的边框排列。
除了 BFC,还有:
- IFC(行级格式化上下文)- inline 内联
- GFC(网格布局格式化上下文)- display: grid
- FFC(自适应格式化上下文)- display: flex或display: inline-flex
注意:同一个元素不能同时存在于两个 BFC 中。
创建
- 根元素
- float 值为 left 、right
- display 值为 inline-block、table-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid
- 绝对定位元素:position 值为 absolute、fixed
- overflow 值不为 visible,即为 auto、scroll、hidden
特点
- 是页面上的一个独立容器,容器里面的子元素不会影响外面的元素
- 垂直方向上,自上而下,与文档流排列方式一致
- 同一 BFC 下的相邻块级元素可能发生margin折叠,创建新的 BFC 可以避免外边距折叠
- BFC区域不会与浮动容器发生重叠
- 两栏布局
- float + overflow:hidden
- 计算BFC高度时,浮动元素参与计算
- 每个元素的左margin和容器的左border相接触,即,每个元素的外边距盒(margin box)的左边与包含块边框盒(border box)的左边相接触(从右向左的格式的话,则相反),即使存在浮动
应用
解决margin重叠问题
- 块级元素的上外边距和下外边距有时会合并(或折叠)为一个外边距,其大小取其中的较大者,这种行为称为外边距折叠(重叠),注意这个是发生在属于同一 BFC 下的块级元素之间
把两个元素变成两个BFC
自适应两栏布局
左列浮动。右列设置 overflow: hidden; 触发BFC , float + overflow:hidden
父子元素的外边距重叠
- 如果在父元素与其第一个/最后一个子元素之间不存在边框、内边距、行内内容,也没有创建块格式化上下文、或者清除浮动将两者的外边距 分开,此时子元素的外边距会“溢出”到父元素的外面。(简单一句话:直接接触,没有border、padding等物理厚度)
解决
- 父元素触发BFC
- 父元素添加border
- 父元素添加padding
清除浮动,解决父元素高度塌陷
- 当容器内子元素设置浮动时,脱离了文档流,容器中总父元素高度只有边框部分高度。
浮动
浮动元素不在文档流中,所以文档流的块框表现得像浮动框不存在
块级元素认为浮动元素不存在,浮动元素会影响行内元素的布局,浮动元素通过行内元素间接影响包含块的布局
浮动元素摆放遵循规则:
- 尽量靠上
- 尽量靠左
- 尽量一个挨一个
- 可能超出包含块
- 不能超过所在行的最高点
- 行内元素绕着浮动元素摆放
带来的问题
- 父元素的高度无法被撑开
- 与浮动元素同级的非浮动元素会紧随其后
清除浮动
- 父级div定义height
- 最后一个浮动元素加空div标签,添加样式clear:both(不推荐)
- 父级div定义zoom
- 父级添加overflow:hidden/auto(不推荐)
- 浮动元素的容器添加浮动(不推荐,会使整体浮动,影响布局)
- after伪元素清除浮动,尾部添加一个看不见的块元素清理浮动,设置clear:both(推荐)
- before和after双伪元素清除浮动
总结 3 种
1、clear属性
2、BFC
3、::after伪元素
定位
- relative:相对自身之前正常文档流中的位置发生偏移,与世隔绝,且原来的位置仍然被占据。发生偏移时,可能覆盖其他元素。body默认是relative,子绝父相。
- absolute:元素框不再占有文档位置,并且相对于包含块进行偏移(所谓包含块就是最近一级外层元素position不为static的元素)。
- fixed:元素框不再占有文档流位置,并且相对于视窗进行定位。
- static:默认值,取消继承。
- sticky:css3新增属性值,粘性定位,相当于relative和fixed的混合。最初会被当作是relative,相对原来位置进行偏移;一旦超过一定的阈值,会被当成fixed定位,相对于视口定位。
- inherit
文档流
定位流
元素的属性 position 为 absolute 或 fixed,它就是一个绝对定位元素。
在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响,而元素具体的位置由绝对定位的坐标决定。
它的定位相对于它的包含块,相关CSS属性:top、bottom、left、right;
对于 position: absolute,元素定位将相对于上级元素中最近的一个relative、fixed、absolute,如果没有则相对于body;
对于 position:fixed,正常来说是相对于浏览器窗口定位的,但是当元素祖先的 transform 属性非 none 时,会相对于该祖先进行定位。
浮动流
在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能地向左边或右边偏移,其效果与印刷排版中的文本环绕相似。
普通流
普通流其实就是指BFC中的FC。FC(Formatting Context),直译过来是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其他元素之间的关系和作用。
在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行。块级元素则会被渲染为完整的一个新行。
除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。
CSS选择器
CSS选择器
| 选择器 | 例子 | 例子描述 |
|---|---|---|
| .class | .intro | 选择 class=“intro” 的所有元素。 |
| .class1.class2 | .name1.name2 | 选择 class 属性中同时有 name1 和 name2 的所有元素。 |
| .class1 .class2 | .name1 .name2 | 选择作为类名 name1 元素后代的所有类名 name2 元素。 |
| #id | #firstname | 选择 id=“firstname” 的元素。 |
| * | * | 选择所有元素。 |
| element | p | 选择所有 元素。 |
| element.class | p.intro | 选择 class=“intro” 的所有 元素。 |
| element,element | div, p | 选择所有div元素和所有p元素。 |
| element element | div p | 选择div元素内的所有p元素。 |
| element>element | div > p | 选择父元素是 div 的所有 直接子 p 元素。 |
| element+element | div + p | 选择紧跟 div 元素的首个同级 p 元素。 |
| element1~element2 | p ~ ul | 选择前面有 p 元素的每个同级的 ul 元素。 |
| [attribute] | [target] | 选择带有 target 属性的所有元素。 |
| [attribute=value] | [target=_blank] | 选择带有 target=“_blank” 属性的所有元素。 |
| [attribute~=value] | [title~=flower] | 选择 title 属性包含单词 “flower” 的所有元素。 |
| [attribute | =value] | [lang |
| [attribute^=value] | a[href^=“https”] | 选择其 src 属性值以 “https” 开头的每个 元素。 |
| [attribute$=value] | a[href$=“.pdf”] | 选择其 src 属性以 “.pdf” 结尾的所有 元素。 |
| [attribute*=value] | a[href*=“w3schools”] | 选择其 href 属性值中包含 “abc” 子串的每个 元素。 |
| :active | a:active | 选择活动链接。 |
| ::after | p::after | 在每个 的内容之后插入内容。 |
| ::before | p::before | 在每个 的内容之前插入内容。 |
| :checked | input:checked | 选择每个被选中的 元素。 |
| :default | input:default | 选择默认的 元素。 |
| :disabled | input:disabled | 选择每个被禁用的 元素。 |
| :empty | p:empty | 选择没有子元素的每个 元素(包括文本节点)。 |
| :enabled | input:enabled | 选择每个启用的 元素。 |
| :first-child | p:first-child | 选择属于父元素的第一个子元素的每个 元素。 |
| ::first-letter | p::first-letter | 选择每个 元素的首字母。 |
| ::first-line | p::first-line | 选择每个 元素的首行。 |
| :first-of-type | p:first-of-type | 选择属于其父元素的首个 元素的每个 元素。 |
| :focus | input:focus | 选择获得焦点的 input 元素。 |
| :fullscreen | :fullscreen | 选择处于全屏模式的元素。 |
| :hover | a:hover | 选择鼠标指针位于其上的链接。 |
| :in-range | input:in-range | 选择其值在指定范围内的 input 元素。 |
| :indeterminate | input:indeterminate | 选择处于不确定状态的 input 元素。 |
| :invalid | input:invalid | 选择具有无效值的所有 input 元素。 |
| :lang(language) | p:lang(it) | 选择 lang 属性等于 “it”(意大利)的每个 元素。 |
| :last-child | p:last-child | 选择属于其父元素最后一个子元素每个 元素。 |
| :last-of-type | p:last-of-type | 选择属于其父元素的最后 元素的每个 元素。 |
| :link | a:link | 选择所有未访问过的链接。 |
| :not(selector) | :not§ | 选择非 元素的每个元素。 |
| :nth-child(n) | p:nth-child(2) | 选择属于其父元素的第二个子元素的每个 元素。 |
| :nth-last-child(n) | p:nth-last-child(2) | 同上,从最后一个子元素开始计数。 |
| :nth-of-type(n) | p:nth-of-type(2) | 选择属于其父元素第二个 元素的每个 元素。 |
| :nth-last-of-type(n) | p:nth-last-of-type(2) | 同上,但是从最后一个子元素开始计数。 |
| :only-of-type | p:only-of-type | 选择属于其父元素唯一的 元素的每个 元素。 |
| :only-child | p:only-child | 选择属于其父元素的唯一子元素的每个 元素。 |
| :optional | input:optional | 选择不带 “required” 属性的 input 元素。 |
| :out-of-range | input:out-of-range | 选择值超出指定范围的 input 元素。 |
| ::placeholder | input::placeholder | 选择已规定 “placeholder” 属性的 input 元素。 |
| :read-only | input:read-only | 选择已规定 “readonly” 属性的 input 元素。 |
| :read-write | input:read-write | 选择未规定 “readonly” 属性的 input 元素。 |
| :required | input:required | 选择已规定 “required” 属性的 input 元素。 |
| :root | :root | 选择文档的根元素。 |
| ::selection | ::selection | 选择用户已选取的元素部分。 |
| :target | #news:target | 选择当前活动的 #news 元素。 |
| :valid | input:valid | 选择带有有效值的所有 input 元素。 |
| :visited | a:visited | 选择所有已访问的链接。 |
CSS选择器优先级
一 、CSS 有多少种样式类型:
- 行内样式:< style/style >
- 内联样式:< div style=”color:red> ;
- 外部样式:< link > 或 @import引入
二 、常见选择器及选择器权重
| 选择器 | 格式 | 优先级权重 |
|---|---|---|
| id选择器 | #id | 100 |
| 类选择器 | .classname | 10 |
| 属性选择器 | a[ref = “eee”] | 10 |
| 伪类选择器 | li:last-child | 10 |
| 标签选择器 | div | 1 |
| 伪元素选择器 | li:after | 1 |
| 相邻兄弟选择器 | h1 + p | 0 |
| 子选择器 | ul > li | 0 |
| 后代选择器 | li a | 0 |
| 通配符选择器 | * | 0 |
三 、注意事项(优先级从高到低)
!important(终极杀器):附加在属性值后面的声明。它会打破所有常规的优先级规则,直接生效。- 内联样式 (Inline Styles):直接写在 HTML 标签里的
style属性(例如<div style="color: red;">)。 - ID 选择器:包含
#的选择器(例如#header)。 - 类 (Class)、属性和伪类选择器:包含
.或[]或:的选择器(例如.active、[type="text"]、:hover)。 - 标签 (Element) 和伪元素选择器:普通的 HTML 标签或双冒号伪元素(例如
div、p、::before)。 - 通配符 (
*) 和组合符 (+,>,~, 空格):它们本身的权重为 0。 - 继承 (Inheritance):父元素传递给子元素的样式,优先级最低(甚至比通配符
*还要低)。
选择器权重Specificity
如何比较两个优先级的高低呢? 比较规则是: 从左往右依次进行比较 ,较大者胜出,如果相等,则继续往右移动一位进行比较 。如果4位全部相等,则后面的会覆盖前面的。
- 内联>id>类=属性=伪类>标签(类型、元素选择器h1)=伪元素
内联样式表的权值最高为 1000;
ID 选择器的权值为 100
Class 类选择器、属性选择器、伪类的权值为 10
HTML 元素选择器、伪元素的权值为 1
加有!important的权值最大,优先级最高
需要注意权值计算要基于选择器的形式。特别是,“[id=p33]”形式的选择器被视为属性选择器(权值为10),即使id属性在源文档的文档类型中被定义为“id选择器”。
- 通配符 * 和关系选择符(+ > ~ ‘’ ||)和否定伪类(:not()) 对优先级没有影响,但是:not内部声明的选择器会影响优先级。
*{} /*通用选择器,权值为0 */ p{color:red;} /*标签,权值为1*/ p span{color:green;} /*两个标签,权值为1+1=2*/ p>span{color:purple;}/*权值与上面的相同,因此采取就近原则*/ a:hover{}/*标签和伪类,权值为1+10=11*/ .warning{color:white;} /*类选择符,权值为10*/ p span .warning{color:purple;} /*权值为1+1+10=12*/ h1+a[rel=up]{}/*标签和属性选择器,权值为1+10=11*/ #footer .note p{color:yellow;} /*权值为100+10+1=111*/ p{color:red!important; } /*优先级最高*/
注意事项
!important优先级最高。避免使用,会破坏样式表中固有的级联规则!!
样式表来源不同时 优先级顺序为:内联>内部>外部>浏览器用户自定义>浏览器默认。
兄弟选择器 li ~ a
相邻兄弟选择器 li + a
属性选择器 a[rel=”external”]
伪类选择器 a:hover, li:nth-child
通配选择器 *{}
类型选择器h1{}
:link :选择未被访问的链接
:visited:选取已被访问的链接
:active:选择活动链接
:hover :鼠标指针浮动在上面的元素
:focus :选择具有焦点的
:first-child:父元素的首个子元素
伪元素选择器 ::before、::after
::first-letter :用于选取指定选择器的首字母
::first-line :选取指定选择器的首行
::before : 选择器在被选元素的内容前面插入内容
::after : 选择器在被选元素的内容后面插入内容
CSS属性继承
继承属性和非继承属性
这一节不用看,与文本、排版、字体相关的基本都会继承;与布局定位相关的,基本不会继承。cursor、visibility是会继承的
继承属性:
- 当元素的一个继承属性 没有指定值时,则取父元素的同属性的计算值
非继承属性:
- 当元素的一个非继承属性没有指定值时,则取属性的初始值
那么这两类属性都有哪些呢?
一、无继承性的属性
1、display:
- 规定元素应该生成的框的类
2、文本属性:
- vertical-align:垂直文本对齐
- text-decoration:规定添加到文本的装饰
- text-shadow:文本阴影效果
- white-space:空白符的处理
- unicode-bidi:设置文本的方向
3、盒子模型的属性:
- width、height、margin 、margin-top、margin-right、margin-bottom、margin-left、border、border-style、border-top-style、border-right-style、border-bottom-style、border-left-style、border-width、border-top-width、border-right-right、border-bottom-width、border-left-width、border-color、border-top-color、border-right-color、border-bottom-color、border-left-color、border-top、border-right、border-bottom、border-left、padding、padding-top、padding-right、padding-bottom、padding-left
4、背景属性:
- background、background-color、background-image、background-repeat、background-position、background-attachment
5、定位属性:
- float、clear、position、top、right、bottom、left、min-width、min-height、max-width、max-height、overflow、clip、z-index
6、生成内容属性:
- content、counter-reset、counter-increment
7、轮廓样式属性:
- outline-style、outline-width、outline-color、outline
8、页面样式属性:
- size、page-break-before、page-break-after
9、声音样式属性:
- pause-before、pause-after、pause、cue-before、cue-after、cue、play-during
二、有继承性的属性
1、字体系列属性
- font:组合字体
- font-family:规定元素的字体系列
- font-weight:设置字体的粗细
- font-size:设置字体的尺寸
- font-style:定义字体的风格
- font-variant:设置小型大写字母的字体显示文本,这意味着所有的小写字母均会被转换为大写,但是所有使用小型大写字体的字母与其余文本相比,其字体尺寸更小。
- font-stretch:对当前的 font-family 进行伸缩变形。所有主流浏览器都不支持。
- font-size-adjust:为某个元素规定一个 aspect 值,这样就可以保持首选字体的 x-height。
2、文本系列属性
- text-indent:文本缩进
- text-align:文本水平对齐
- line-height:行高
- word-spacing:增加或减少单词间的空白(即字间隔)
- letter-spacing:增加或减少字符间的空白(字符间距)
- text-transform:控制文本大小写
- direction:规定文本的书写方向
- color:文本颜色
3、元素可见性:
- visibility
4、表格布局属性:
- caption-side、border-collapse、border-spacing、empty-cells、table-layout
5、列表布局属性:
- list-style-type、list-style-image、list-style-position、list-style
6、生成内容属性:
- quotes
7、光标属性:
- cursor
8、页面样式属性:
- page、page-break-inside、windows、orphans
9、声音样式属性:
- speak、speak-punctuation、speak-numeral、speak-header、speech-rate、volume、voice-family、pitch、pitch-range、stress、richness、、azimuth、elevation
三、所有元素可以继承的属性
1、元素可见性:
- visibility
2、光标属性:
- cursor
四、内联元素可以继承的属性
1、字体系列属性
2、除text-indent、text-align之外的文本系列属性
五、块级元素可以继承的属性
1、text-indent、text-align
不可/可继承属性
可继承
- 字体属性
- 文本属性
- 元素可见性
- 列表布局属性
- 光标属性
不可继承
- 盒子模型属性
- display
- 背景属性
- 定位属性
- 生成内容、轮廓样式、页面样式、声音样式
line-height 如何继承
- 父元素的 line-height 写了具体数值,比如 30px,则子元素 line-height 继承该值。
- 父元素的 line-height 写了比例,比如 1.5 或 2,则子元素 line-height 也是继承该比例。
- 父元素的 line-height 写了百分比,比如 200%,则子元素 line-height 继承的是父元素 fontSize * 200% 计算出来的值。
CSS常考属性
CSS隐藏元素的方法有哪些
常见的隐藏属性的方法有 display: none 与 visibility: hidden:
- display: none:渲染树不会包含该渲染对象,因此该元素不会在页面中占据位置,也不会响应绑定的监听事件。
- visibility: hidden:元素在页面中仍占据空间,但是不会响应绑定的监听事件。
- opacity: 0:将元素的透明度设置为0,以此来实现元素的隐藏。元素在页面中仍然占据空间,并且能够响应元素绑定的监听事件。
- position: absolute:通过使用绝对定位将元素移除可视区域内,以此来实现元素的隐藏。
- z-index: 负值:来使其他元素遮盖住该元素,以此来实现隐藏。
- clip/clip-path:使用元素裁剪的方法来实现元素的隐藏,这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。
- transform: scale(0,0):将元素缩放为0,来实现元素的隐藏。这种方法下,元素仍在页面中占据位置,但是不会响应绑定的监听事件。
在VUE中还有 v-if 和 v-show 这两个指令,在目录你能看到关于这两个的使用区别
display: none与 visibility: hidden的区别
这两个属性都是让元素隐藏,不可见。两者的区别主要分两点
1 、是否在渲染树中
- display: none 会让元素完全从渲染树中消失,渲染时不会占据任何空间;
- visibility: hidden 不会让元素从渲染树中消失,渲染的元素还会占据相应的空间,只是内容不可见。
2 、是否是继承属性
- display: none 是非继承属性,子孙节点会随着父节点从渲染树消失,通过修改子孙节点的属性也无法显示;
- visibility: hidden 是继承属性,子孙节点消失是由于继承了 hidden,通过设置 visibility: Avisible可以让子孙节点显示;
- 修改常规文档流中元素的 display 通常会造成文档的重排,但是修改 visibility 属性只会造成本元素的重绘
- 如果使用读屏器,设置为 display: none 的内容不会被读取,设置为 visibility: hidden 的内容会被读取。
这两者的关系类似于 v-if 和 v-show 之间的关系
文本溢出
单行溢出
- text-overflow:当文本溢出时,显示省略符号代表被修剪的文本
- white-space:设置文字在一行显示,不能换行
- overflow:文字长度超出限定宽度,隐藏超出的内容
overflow:hidden,普通情况用在块级元素的外层隐藏内部溢出元素,或配合以下两个属性实现文本溢出省略
white-space:nowrap,设置文本不换行,是overflow:hidden和text-overflow:ellipsis生效的基础
text-overflow属性值如下:
- clip:对象内文本溢出部分裁掉
- ellipsis:对象内文本溢出时显示…
多行溢出
基于高度截断(现在用的很少)
伪元素+定位
通过伪元素绝对定位到行尾并遮住文字,再通过overflow:hidden,隐藏多余文字
优点
- 兼容性好
- 响应式截断,根据不同宽度做出调整
基于行数截断
1 | |
background-size
设置背景图片大小。图片可以保有其原有的尺寸、拉伸到新的尺寸,或者在保持原有比例的同时缩放到元素的可用空间的尺寸
1 | |
属性
100%:整个图片铺满div
cover:整个图片铺满div,缩放背景图片以完全覆盖背景区,可能背景图片部分看不见。和 contain 相反,cover 尽可能大地缩放背景图像并保持图像的宽高比例(图像不会被压扁)。背景图以它的全部宽或者高覆盖所在容器。当容器和背景图大小不同时,背景图的 左/右 或者 上/下 部分会被裁剪
contain:不能铺满整个div,缩放背景图片以完全装入背景区,可能背景区部分空白。contain 尽可能地缩放背景并保持图像的宽高比例(图像不会被压缩)。背景图会填充所在容器。当背景图和容器的大小的不同时,容器的空白区域(上/下或者左/右)显示由 background-color 设置的背景颜色
auto:不能铺满整个div,以背景图片比例缩放背景图片
| 属性值 | 核心行为 | 图片会变形吗? | 图片会被裁剪吗? | 盒子会有留白吗? |
|---|---|---|---|---|
cover |
填满容器为主 | ❌ 否 | ✅ 会 | ❌ 否 |
contain |
完整显示为主 | ❌ 否 | ❌ 否 | ✅ 会 |
100% 100% |
强行拉伸填满 | ✅ 会 | ❌ 否 | ❌ 否 |
z-index
取值
数字无单位
- auto(默认)和其父元素一样的层叠等级
- 整数
- inherit继承
层叠上下文和层叠等级
对每一个网页来说,默认创建一个层叠上下文(可类比一张桌子),这个桌子就是html元素,html元素的所有子元素都位于这个默认层叠上下文中的某个层叠等级(类似于桌子上放了一个盆儿,盆上放了一个果盘,果盘上放了一个水杯……)
将元素的z-index属性设置为非auto时,会创建一个新的层叠上下文,它及其包含的层叠等级独立于其他层叠上下文和层叠等级(类似于搬了一张新桌子过来,和旧桌子完全独立)
层叠顺序
div默认在html之上,及div的层级高于html
一个层叠上下文可能出现7个层叠等级,从低到高排列为
- 背景和边框
- z-index为负数
- block盒模型(位于正常文档流,块级,非定位)
- float盒模型(浮动,非定位)
- inline盒模型(位于正常文档流,内联,非定位)
- z-index=0
- z-index为正数(数值越大越靠上方)
压盖顺序
自定义压盖顺序:使用属性z-index
- 只有定位元素(position属性明确设置为absolute、fixed或relative)可使用z-index
- 如果z-index值相同,html结果在后面的压盖住在前面的
- 父子都有z-index,父亲z-index数值小时,儿子数值再大没用
client、offset&scroll
client 主要与可视区有关
客户区大小指的是元素内容及其内边距所占据空间大小。
offset 主要与自身有关
偏移量,可动态得到元素的位置(偏移),大小。**元素在屏幕上占用的所有可见空间。**元素高度宽度包括内边距,滚动条和边框。
offsetParent是一个只读属性,返回一个指向最近的(closest,指包含层级上的最近)包含该元素的定位元素。如果没有定位的元素,则 offsetParent 为最近的 table, td, th或body元素。当元素的 style.display 设置为 “none” 时,offsetParent 返回 null。
| 家族 | Width / Height 包含什么? | Top / Left 表示什么? | 最常用于 |
|---|---|---|---|
client |
内容 + 内边距 (不含边框/滚动条) | 上/左边框的厚度 | 计算元素内部可视区域大小 |
offset |
内容 + 内边距 + 边框 + 滚动条 | 距离定位父元素的物理距离 | 获取元素实际物理大小、拖拽计算 |
scroll |
完整内容的真实大小 (含被隐藏部分) | 滚动条卷去的距离 | 监听滚动到底部、实现返回顶部 |
e.clientX鼠标指针距离浏览器**可视窗口(Viewport)**左边缘的 X 坐标。
e.clientY鼠标指针距离浏览器**可视窗口(Viewport)**上边缘的 Y 坐标。
scroll 滚动系列
动态获得元素大小,滚动距离等。具有兼容问题。
- scrollWidth 和 scrollHeight 主要用于确定元素内容的实际大小
scrollTop:元素的内容顶部,被隐藏在可视区域(视口)上方的像素值。scrollLeft:元素的内容左侧,被隐藏在可视区域左侧的像素值。- scrollLeft 和 scrollTop 属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位置
- 垂直滚动 scrollTop > 0
- 水平滚动 scrollLeft > 0
- 将元素的 scrollLeft 和 scrollTop 设置为 0,可以重置元素的滚动位置
共同点
返回数字时,均不带单位。
只读。
CSS3新特性
- 新增选择器: p:nth-child(n){color: rgba(255, 0, 0, 0.75)}
- 弹性盒模型: 弹性布局: display: flex; 栅格布局: display: grid;
- 渐变 线性渐变: linear-gradient(red, green, blue); 径向渐变:radial-gradient(red, green, blue)
- 边框 border-radius:创建圆角边框 box-shadow:为元素添加阴影 border-image:使用图片来绘制边框
- 阴影 box-shadow:3px 3px 3px rgba(0, 64, 128, 0.3);
- 背景 用于确定背景画区:background-clip 设置背景图片对齐:background-origin 调整背景图片的大小background-size 控制背景怎样在这些不同的盒子中显示:background-break 多列布局: column-count: 5;
- text-overflow 文字溢出时修剪:text-overflow:clip 文字溢出时省略符号来代表:text-overflow:ellipsis
- transform 转换 transform: translate(120px, 50%):位移 transform: scale(2, 0.5):缩放 transform: rotate(0.5turn):旋转 transform: skew(30deg, 20deg):倾斜
- animation 动画 animation-name:动画名称 animation-duration:动画持续时间 animation-timing-function:动画时间函数 animation-delay:动画延迟时间 animation-iteration-count:动画执行次数,可以设置为一个整数,也可以设置为infinite,意思是无限循环 animation-direction:动画执行方向 animation-paly-state:动画播放状态 animation-fill-mode:动画填充模式
还有多列布局、媒体查询(@media)、混合模式等等,而CSS3如今有很多是我们日常使用到的,比如我们在处理文字溢出时会使用 text-overflow ,比如我们需要文字突破限制12像素就可以使用 transform: scale(0.5)来调整。
flex布局
模块一:Flex 弹性布局(最强排版工具)
核心概念: 开启 Flex 后,子元素的 float、clear 和 vertical-align 全部失效。它分为**父容器(管大局)和子容器(管自己)**两套属性。
1. 父容器属性(决定整体怎么排)
flex-direction(主轴方向): 决定子元素是从左到右(row)、从右到左、还是从上到下(column)排。flex-wrap(是否换行): 默认死都不换行(nowrap),哪怕把子元素挤扁。设为wrap就可以正常换行。flex-flow(复合写法): 上面两个属性的缩写。比如flex-flow: row wrap;。justify-content(主轴对齐): 控制子元素在主轴(通常是水平)上的位置。常用center(居中)、space-between(两端对齐)、space-around(分散对齐)。align-items(单行交叉轴对齐): 控制子元素在交叉轴(通常是垂直)上的位置。常用center(垂直居中)、stretch(默认拉伸填满高度)。align-content(多行交叉轴对齐): 只有当flex-wrap: wrap且有多行元素时才生效,用来控制行与行之间的对齐。
2. 子容器属性(决定个体怎么变)
order(排序): 默认 0,越小越靠前,甚至可以写负数。flex-basis(基准大小): 在分配多余空间前,元素占据的初始尺寸(默认auto)。flex-grow(放大比例): 默认 0。如果有剩余空间,按什么比例瓜分/撑大。flex-shrink(缩小比例): 默认 1。如果空间不够,按什么比例牺牲/缩小(设为 0 表示死也不缩小)。flex(核心复合属性): 面试最爱考!它是grow、shrink、basis的简写。flex: 1等同于flex: 1 1 0%(等分剩余空间,最常用)。flex: auto等同于flex: 1 1 auto。flex: none等同于flex: 0 0 auto(尺寸固定,绝对不伸缩)。
align-self(搞特殊): 允许单个子元素拥有和align-items不同的对齐方式(比如大家都在上面,只有它在最底下)。需要注意align-self作用的是交叉轴方向,order是水平justify-self:同上,作用于子元素,水平方向
模块二:隐藏元素的 6 种方案(解密乱码表格)
| 隐藏方案 | 占据页面空间吗? | 触发重排(Reflow)? | 触发重绘(Repaint)? | 能响应点击事件吗? | 适用场景 |
|---|---|---|---|---|---|
display: none |
❌ 否 (彻底消失) | ✅ 会 | ✅ 会 | ❌ 否 | 彻底移除元素,如切换 Tab |
visibility: hidden |
✅ 是 (留个坑位) | ❌ 否 | ✅ 会 | ❌ 否 | 占位隐藏,保持布局稳定 |
opacity: 0 |
✅ 是 (变透明) | ❌ 否 | 会 (GPU动画除外) | ✅ 能 | 淡入淡出动画 |
width/height: 0 |
❌ 否 (缩成点) | ✅ 会 | ✅ 会 | ❌ 否 | 折叠面板动画 |
position: absolute |
❌ 否 (移出屏幕) | ✅ 会 (仅自身) | ✅ 会 | ❌ 否 | 将元素丢到 -9999px 之外 |
transform: scale(0) |
✅ 是 (保留原坑位) | ❌ 否 | ✅ 会 | ❌ 否 | 缩放消失动画 (性能极好) |
模块三:opacity vs rgba 的核心本质
这个区别其实只需要一句话就能讲透:一个是“物理外挂”,一个是“局部附魔”。
opacity: 0.5;(物理外挂/连坐机制): 它作用于整个 DOM 节点。你给父元素设置了半透明,里面的所有文字、图片、子元素统统跟着半透明。子元素想通过opacity: 1抢救一下都救不回来(因为继承了外层的透明度)。background: rgba(255,0,0,0.5);(局部附魔): 它只是一种颜色值!仅仅让这个盒子的背景色变成半透明,里面的文字和子元素完全不受影响,依然是 100% 清晰可见的。
页面中不存在存在存在重排会不会不会重绘会会不一定自身绑定事件不触发不触发可触发transition不支持支持支持子元素可复原不能能不能被遮挡的元素可触发事件能能不能opacity和rgba区别
常见布局
两栏布局
左边宽度固定,右边宽度自适应。
- 利用flex布局,将左边元素设置为固定宽度200px,将右边的元素设置为flex:1
- 利用浮动。左边元素宽度设置为200px,且设置向左浮动。右边元素的margin-left设置为200px,宽度设置为auto(默认为auto,撑满整个父元素)。margin-left/padding-left/calc
- 利用浮动。左边元素宽度固定 ,设置向左浮动。右侧元素设置 overflow: hidden; 这样右边就触发了 BFC ,BFC 的区域不会与浮动元素发生重叠,所以两侧就不会发生重叠。 float + overflow:hidden
- 左列左浮动,将自身高度塌陷,使得其它块级元素可以和它占据同一行位置
- 右列利用自身流特性占满整行
- 右列设置overflow,触发BFC特性,使自身和左列浮动元素隔开,不沾满整行
- 绝对定位 父级相对定位 左边absolute定位,宽度固定。设置右边margin-left为左边元素的宽度值。
- 绝对定位,父级元素相对定位。左边元素宽度固定,右边元素absolute定位,left为宽度大小,其余方向为0。(有歧义,谨慎使用!)
- 使用 calc 计算
.left { display: inline-block; width: 240px; } .right { display: inline-block; width: calc(100% - 240px); } //使用 calc() 函数计算 <div> 元素的宽度
- grid
1 | |
三栏布局
flex弹性布局
利用flex布局,左右两栏设置固定大小,中间一栏设置为flex:1
1 | |
grid栅格布局
1 | |
优点:网格布局作为一个比较超前一点的布局自然有其独特的魅力,其布局方式布局思维都可以让你眼前一亮,有种新的思想。
缺点:兼容性
grid-template-columns 该属性是基于 网格列 的维度,去定义网格线的名称和网格轨道的尺寸大小。
用单位 fr 来定义网格轨道大小的弹性系数。 每个定义了 的网格轨道会按比例分配剩余的可用空间。当外层用一个 minmax() 表示时,它将是一个自动最小值(即 minmax(auto, ) ) 。
垂直居中
top属性的本质是设置“定位元素上边缘”与“包含块上边缘”的偏移量。它必须配合position才能生效(除了static)
- display: flex; justify-content:center; align-items:center
- display: grid; place-items: center;
- absolute+margin auto。因为宽高固定,对应方向实现平分;
- absolute+transform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<div style="position: relative; height: 200px; background-color: #eee">
<div
style="
position: absolute;
width: 100px;
height: 100px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background-color: aliceblue;
"
></div>
</div>
<div style="position: relative; height: 100px; background-color: aliceblue">
<div
style="
position: absolute;
top: 50%;
left: 50%;
height: 50px;
transform: translate(-50%, -50%);
background-color: #007bff;
"
>Lufi</div>
</div>
<div style="height:25px; line-height: 25px; text-align: center;">
<a>??</a>
</div>
水平居中
不定宽高
- 定位+margin:auto
- 定位+transform
- 定位+margin:负值
- flex布局
- grid布局
水平垂直居中
- 定位 + margin: auto
- 定位 + margin: 负值
- 定位 + transform
- table布局
- flex布局
- grid布局
div居中的几种方式
同上
CSS画三角形
📱 移动端适配两大核心任务
移动端适配本质上只解决两个问题:
宏观布局(大框架):保证不同尺寸手机上,元素比例一致。
微观细节(1px 问题):解决高清屏(DPR > 1)下,1px 边框被渲染得太粗的问题。
📏 一、 1px 极细线解决方案(微观)
直接写 0.5px 会导致老手机边框消失或变粗(兼容性差),因此需要以下方案:
🌟 方案 1:伪元素 + 缩放
scale(目前最推荐、最常用)做法: 目标元素设为相对定位,利用
::after创建一个宽长200%的绝对定位伪元素。给伪元素加上标准的1px边框,最后用transform: scale(0.5)将其整体缩小一半。优点: 兼容性完美,只影响单一边框,不干扰页面其他布局。
1
2
3
4
5
6
7
8
9
10
11
12.triangle::after {
position:absolute;
content: "";
width: 200%;
height: 200%;
border: 1px solid red;
transform: scale(0.5);
transform-origin: 0 0;
}
<div class="triangle" style="position: relative; width: 200px; height: 100px; background-color: aliceblue;">
</div>❌ 方案 2:动态 Viewport + REM(flexible.js 原理,已弃用)
做法: 用 JS 获取设备的 DPR,动态修改
<meta viewport>的缩放比(如initial-scale=0.5)。缺点: 全局暴力缩放,会导致引入的第三方 UI 组件库(如 Vant)全部变小,维护成本极高,现已淘汰。
📐 二、 整体屏幕尺寸适配(宏观)
🚀 现代新标准:VW 单位适配(推荐)
做法: 完全抛弃 JS 计算。直接使用 CSS 原生的
vw单位(1vw = 屏幕宽度的 1%)。优势: 浏览器原生支持,配合 PostCSS 插件可实现设计稿
px到vw的全自动转换。
(## postcss-px-to-viewport)
🕰️ 传统老方案:REM 适配
做法: 依靠 JS 监听屏幕宽度,动态计算并修改
<html>的font-size,页面元素全部使用rem单位。劣势: 依赖 JS 执行,存在加载闪烁风险,已被 VW 替代。
使chrome支持12px以下文字
现在已经不存在这个限制了
1 | |
响应式布局
概念:页面的设计和开发根据用户行为和设备环境进行调整和响应
1 | |
- width=device-width: 自适应手机屏幕的尺寸宽度
- maximum-scale:缩放比例的最大值
- inital-scale:缩放的初始化
- user-scalable:用户可以缩放
实现响应式布局的方式
媒体查询
百分比
vw/vh
rem
缺点:仅适用布局、信息、框架并不复杂的部门类型网站
兼容各种设备工作量大,效率低下
代码累赘,出现隐藏无用的元素,加载时间加长
一定程度上改变了网站原有的布局结构
媒体查询
1 | |
媒体查询可以让我们针对不同的媒体类型定义不同的样式,当重置浏览器窗口大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。
- 媒体类型 (Media Types): 定义设备的大类。
screen:最常用,代表所有带屏幕的设备(电脑、手机、平板)。print:打印预览模式(非常实用!常用于网页打印时,隐藏掉不必要的导航栏或广告)。all:所有设备(默认值,通常可省略)。
- 媒体特性 (Media Features): 具体的触发条件。
max-width:最大宽度(小于等于该宽度时生效,常用于从大屏往小屏写)。min-width:最小宽度(大于等于该宽度时生效,常用于从小屏往大屏写)。orientation: portrait:设备处于竖屏状态。orientation: landscape:设备处于横屏状态。
- 逻辑操作符:
and:并且。例如@media screen and (min-width: 768px)。,(逗号):或者 (OR)。例如@media (max-width: 500px), (orientation: portrait)。not:否定,排除某种设备。
百分比
通过百分比单位,可以使得浏览器中组件的宽和高随着浏览器的高度的变化而变化,从而实现响应式的效果。
vw/vh
px、em、rem、vh、vw的区别及使用场景
这几个单位是在写长度的时候常常会用到的:
一、px
px:也就是像素,是基于屏幕分辨率来说的,一旦设置了,就无法适应页面大小的变化。
二、em
em:是相对单位,相对于当前对象内文本的字体大小(也就是它的父元素),如果当前对象内文本的字体没有设置大小,就会相对于浏览器默认字体大小也就是16px。所以在没有设置的情况下 1em = 16px。
为了便于运算你可以在body选择器中声明Font-size=62.5%;这就使em值变为 16px*62.5%=10px, 这样12px=1.2em, 10px=1em, 也就是说只需要将你的原来的px数值除以10,然后换上em作为单位就行了。
三、rem
rem:rem的出现是为了解决em的问题的,em是相对于父元素来说的,这就要求我们进行任何元素设置的时候,都需要知道它的父元素字体的大小。而 rem是相对于根元素,这样就意味着,我们只需要在根元素确定一个参考值,就可以了,同时还能做到只修改根元素就成比例地调整所有字体大小。
四、vh、vw
vh、vw:vw 是根据窗口的宽度。会把窗口的大小分为100份,所以50vw代表窗口大小的一半。并且这个值是相对的,当窗口大小发生改变也会跟着改变,同理,vh则为窗口的高度
四、总的来说,四者的区别:
px 是固定的大小,em 是相对于父元素字体的大小,rem 是相对于根元素字体的大小,vh、vw 是相对可是窗口的大小
五、使用场景的区别:
- 一般我们在设置边框和边距的时候用 px 比较好
- 而在一些需要做响应式的页面用 rem 比较便捷
- 但是具体还是得看你的业务来定的
rem布局
rem单位都是相对于根元素html的font-size来决定大小的,根元素的font-size相当于提供了一个基准,当页面的size发生变化时,只需要改变font-size的值,那么以rem为固定单位的元素的大小也会发生响应的变化。
过渡与动画
1. 样式层动画:CSS3 Transition 与 Animation
这是目前日常业务开发中使用频率最高的阵营,主打一个“脱离 JS 主线程,利用浏览器底层渲染”。
Transition(过渡): 它是**“状态 A 到状态 B”的单程票**。它不能自己动,必须由某个事件(如鼠标悬停
:hover、JS 添加类名例如toggle)触发。- 为什么加了
transform: translate3d(0,0,0)就不卡了?因为这行代码开启了硬件加速(GPU 加速)。它会强制浏览器为这个元素创建一个独立的“复合图层(Compositor Layer)”。在这个图层里的移动和缩放,不会引发整个页面的“重排重绘”,而是直接交由显卡处理,极其丝滑。
- 为什么加了
Animation(动画): 它是真正意义上的独立动画。依靠
@keyframes定义关键帧(0%, 50%, 100%),元素可以自动执行,且可以无限循环、来回往复。它极大地解放了 JS 的计算压力。与 JavaScript动画使用定时器修改不同,CSS动画是通过指定 @keyframes 来指定动画效果,然后绑定到需要实习的元素上。@keyframes:规则中指定了 CSS 样式,动画将在特定时间逐渐从当前样式更改为新样式。animation-name:用来绑定动画规则animation-duration:定义需要多长时间才能完成动画animation-delay:规定动画开始的延迟时间。也就是多久后才开始动画animation-iteration-count:指定动画应运行的次数。animation-direction:指定是向前播放、向后播放还是交替播放动画。animation-timing-function:规定动画的速度曲线。animation-fill-mode:SS 动画不会在第一个关键帧播放之前或在最后一个关键帧播放之后影响元素。animation-fill-mode 属性能够覆盖这种行为。animation:实现与上例相同的动画效果,也就是类似我们的 font 可以包含所有的相关属性
2. 脚本层动画:传统 JS 与 requestAnimationFrame (rAF)
当 CSS 无法满足复杂的逻辑交互时(比如跟随鼠标拖拽、物理抛物线),我们必须用 JS 介入。** 实践中一般需要设置 style = “position: relative” 创建容器元素。通过 style = “position: absolute” 创建动画元素。然后通过定时器(绘图函数)来控制动画元素的变化,也就是按照我们的规则来修改 CSS样式。
传统 JS 直接操纵 DOM(已淘汰): 以前用
setInterval不断去改变元素的style.left或style.width。- 致命缺点: 会导致页面疯狂触发重排(Reflow)和重绘(Repaint)。而且
setInterval的执行时机不准,经常和显示器的刷新频率对不上,导致“掉帧”和画面撕裂。
- 致命缺点: 会导致页面疯狂触发重排(Reflow)和重绘(Repaint)。而且
Web API 新星:
requestAnimationFrame(rAF)原理: 它是浏览器专门为动画提供的 API。它就像是浏览器的“心跳”,完全跟着显示器的刷新节奏走(通常是 60Hz,即每 16.6ms 唤醒一次)。
碾压优势: 第一,极致流畅,不掉帧;第二,极其省电。当你把网页切换到后台标签页时,rAF 会自动暂停执行,而传统的
setInterval还在后台疯狂空转消耗 CPU。
3. 图形层动画:SVG 与 Canvas
当你的动画不再是简单的方块移动,而是涉及复杂的图形、图表或游戏时,传统的 DOM 元素就吃不消了。
SVG(矢量图): 本质上它依然是 DOM 树的一部分。
- 特点: 放大绝对不会模糊。它自带丰富的动画标签(如
<animateMotion>可以让图形沿着特定的不规则路径移动,这用纯 CSS 极难实现)。适合做:复杂的图标动画、路径描边动画。
- 特点: 放大绝对不会模糊。它自带丰富的动画标签(如
Canvas(画布): 它是 HTML5 提供的“像素画板”。
特点: 它在 HTML 里只有一个
<canvas>标签(一个 DOM 节点)。里面成千上万的图形全靠 JS 调用 API 逐个像素“画”出来。碾压优势: 极高的性能。如果页面上有 10000 个乱飞的粒子,用 DOM(即使是 SVG)绝对会卡死浏览器,但在 Canvas 里却能轻松应对。适合做:网页小游戏、酷炫的粒子特效背景、数据可视化大屏。
JavaScript 动画 和 CSS动画
JS动画优点:
过程可控,在动画中可以实现任何效果,暂停,返回,加速等等动画效果丰富,可以根据绘图函数实现任意效果,跳跳球,变速等兼容性好,使用 CSS3 存在兼容问题,但是 JavaScript 几乎没有
缺点:
- JavaScript 在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况。
- 代码的复杂度高于CSS动画。
CSS动画
优点(浏览器可以对动画进行优化):
- 集中所有DOM,一次重绘重排,刷新频率和浏览器刷新频率相同。
- 代码简单,方便调试
- 不可见元素不参与重排,节约CPU
- 可以使用硬件加速(通过 GPU 来提高动画性能)。
缺点:
- 运行过程控制较弱,无法附加事件绑定回调函数。
- 代码冗长。
SCSS
1、 嵌套
$baseColor: red; $color1: green; body{ background-color: $baseColor; $color2: yellow; div{ background-color:$color2; color: $color1; } div2{ background-color:$color2; color: $color1; } } //编译后 body { background-color: red; } body div { background-color: yellow; color: green; } body div2 { background-color: yellow; color: green; }
2、 定义变量
$baseColor: red; $color1: green; body{ background-color: $baseColor; } div{ background-color:$baseColor; color: $color1; } //编译出的结果 body { background-color: red; } div { background-color: red; color: green; }
3、 混合
@mixin box-sizing($sizing) { -webkit-box-sizing: $sizing; -moz-box-sizing: $sizing; box-sizing: $sizing; } .box-border{ border: 1px solid red; @include box-sizing(border-box) } //编译结果 .box-border { border: 1px solid red; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
4、 继承
.A{ color: red; border: #0D47A1 2px solid; background-color: #0e0e0e; } .B{ @extend .A; } .C{ @extend .A; border-color: #00a507; } //编译结果 .A, .B, .C { color: red; border: #0D47A1 2px solid; background-color: #0e0e0e; } .C { border-color: #00a507; }
CSS性能优化
- 内联首屏关键CSS
- 异步加载CSS
- 资源压缩
- 合理使用选择器
- 减少使用昂贵的属性
- 不要使用@import
- CSS雪碧图
- 小图片转为BASE64编码