# CSS 知识集锦

# 一、层叠上下文
普通元素的层叠等级优先由其所在的层叠上下文决定。
层叠等级的比较只有在当前层叠上下文元素中才有意义。不同层叠上下文中比较层叠等级是没有意义的。
# 层叠上下文创建
根元素
html
创建的层叠上下文postion
创建的层叠上下文普通元素设置
position
属性为非static
值并设置z-index
属性为具体数值,产生层叠上下文。CSS3
创建的层叠上下文opacity
不为 1filter
不为none
transform
不为none
z-index
不为auto
的flex
元素( 父元素display : flex | inline-flex
且子元素z-index
为数值 )
注意
z-index: auto
和z-index: 0
两个数值上面是相等的, 但是意义却不一样; position
非 static
且 z-index: 0
会产生层叠上下文, 而此时z-index: auto
不产生
# 元素层叠顺序
同一层叠上下文的层叠等级才能进行比较, 同一层叠上下文中的元素还需要比较层叠顺序来确定最终的层叠等级, 层叠顺序从小到大依次为:
background/border
最小z-index
负值block
元素float
元素inline/inline-block
元素z-index=auto
或者css3
默认的层叠上下文z-index
正值
注意
当元素的层叠等级一致、层叠顺序也相同的时候,在 DOM 流中处于后面的元素会覆盖前面的元素
# 层叠等级比较
首先比较两个元素是否处于同一个层叠上下文中
同一层叠上下文比较多个子元素层叠顺序
同一层叠上下文同一层叠顺序, 层叠等级大,显示在上面
不在同一层叠上下文中,请先比较他们所处的父级层叠上下文的层叠等级
当两个元素层叠等级相同、层叠顺序相同时,在 DOM 结构中后面的元素层叠等级在前面元素之上。
# 二、百分比的相对性
em
相对于当前元素或当前元素继承来的字体的宽度, 对于 font-size
来说,em
相对于父元素的字体大小;line-height
中,em
却相对于自身字体的大小
position: relative
中的百分比是相对于相对与自身的,left
相对于自己的width
,top
相对于自己的height
position: fixed
中的 的百分比是相对于视口的,left
相对于视口的width
,top
相对于视口的height
margin
和padding
的百分比是相对于父元素的宽度border-radius
的百分比是相对于自身宽高background-size
的百分比是相对于自身宽高transform: translate
的百分比是相对于自身宽高text-indent
的百分比是相对于父元素的宽度width
font-size
的百分比是相对于父元素的字体font-size
数值line-height
的百分比是相对于该元素的font-size
数值
# 三、行高继承规则
父元素设置了line-height
, 子元素应该如何继承, 主要分为以下三种情况:
父元素的
line-height
是具体数值, 那么子元素直接继承该值父元素的
line-height
是比例值(如: 1.5), 则继承该比例值, 再根据子元素的font-size
大小计算父元素的
line-height
是百分比(如: 200%), 则先通过父元素font-size
计算line-height
具体值后再继承
# 四、块级格式上下文
BFC(Block formatting context)
直译为"块级格式化上下文"。它是一个独立的渲染区域,只有 Block-level box 参与, 它规定了内部的 Block-level Box 如何布局,并且与这个区域外部毫不相干
# BFC 的生成
CSS2.1 中规定满足下列 CSS 声明之一的元素便会生成 BFC
根元素
float
的值不为none
overflow
的值不为visible
display
的值为inline-block
、table-cell
、table-caption
position
的值为absolute
或fixed
注意
display:table
也认为可以生成 BFC,其实这里的主要原因在于 Table 会默认生成一个匿名的 table-cell
,正是这个匿名的 table-cell
生成了 BFC
# BFC 的约束规则
内部的 Box 会在垂直方向上一个接一个的放置
垂直方向上的距离由 margin 决定。(完整的说法是:属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠(塌陷),与方向无关。)
每个元素的左外边距与包含块的左边界相接触(从左向右),即使浮动元素也是如此。(这说明 BFC 中子元素不会超出他的包含块,而 position 为 absolute 的元素可以超出他的包含块边界)
BFC 的区域不会与 float 的元素区域重叠(两栏布局应用)
计算 BFC 的高度时,浮动子元素也参与计算(解决父元素高度塌陷)
BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然
# 五、样式表层叠规则
CSS
中样式是具有层叠规则的, 通过层叠规则来判定样式的优先级, 可用来解决样式冲突, 层叠从上到下的规则为:
# 优先级
作者样式表包含!import > 默认样式表包含!import > 作者样式表 > 默认样式表
# 特殊性
选择器的特殊性是由四位数字[w, x, y, z]组成
第一位 w 代表是否行内样式(1 表示是, 0 表示否)
第二位 x 代表
ID选择器
的数量和第三位 y 代表
类选择器
、伪类选择器
属性选择器
的个数和最后以为 z 代表
元素选择器
和伪元素选择器
的数量和
# 源次序
源代码中的书写顺序即为源次序, 样式表中, 如果优先级和特殊性都一致的情况下, 就看在代码中的书写先后顺序, 后写的覆盖前面
# 六、3D效果技巧
使用CSS3D效果, 可以使元素在三维空间中的动态变换, 为网页带来令人惊叹的交互体验。实现3D效果的规则主要有三条
# 嵌套HTML
首先准备三层嵌套的html
结构
- 最外层的
wrapper-3d
用于设置景深 - 中间一层的
container
用于设置子元素处于3D平面 - 最后一层的
child
用于设置3D的变换
<div class="wrapper-3d">
<div class="container">
<div class="item child-front">front</div>
<div class="item child-middle">middle</div>
<div class="item child-bottom">bottom</div>
</div>
</div>
# 设置景深和3D平面
.wrapper-3d
上设置视角景深perspective
.container
元素上设置transform-style
.wrapper-3d {
width: 200px;
height: 200px;
/* 设置景深, 正值贴近观察者, 负值远离观察者 */
perspective: 600px;
}
.container {
width: 100%;
height: 100%;
position: relative;
/* 设置子元素处于3D平面 */
transform-style: preserve-3d;
}
# 设置3D变换
给所有3D子元素上设置绝对定位和设置各个方向的3D变换(平移、旋转等等)
.item {
position: absolute;
width: 200px;
height: 200px;
}
.child-front { transform: translateZ(-100px) rotateY( 0deg); }
.child-middle { transform: translateZ(-100px) rotateX( -90deg); }
.child-bottom { transform: translateZ(-100px) rotateX( 90deg); }
总的来说, 爷爷元素设置perspective
、父亲元素设置transform-style: preserve-3d
、子元素们设置绝对定位和3D变换
# 七、检查溢出元素
在开发过程中,我们经常会遇到元素溢出的情况,这时候我们需要检查元素是否溢出,如果溢出了,我们还需要知道溢出的方向,这样才能更好的解决问题
那么,如何快速找到导致溢出行为的元素呢?
可以使用一个CSS
属性outline
, 与border
所不同的是outline
不会影响元素的大小和位置,它只是在元素周围绘制一条线,用于突出显示元素的边框
outline 与 border 的区别
- 前者不占据空间,绘制于元素内容周围, 而
border
会占据一定的空间,可能会改变元素的大小和位置 - 根据规范,
outline
通常是矩形,但也可以是非矩形的。
/* 可以给所有元素设置一个outline */
* {
outline: 1px solid red !important;
}
通过添加上述的CSS
属性, 就可以非常直观的发现溢出元素包含块的目标元素
# 八、默认属性值
CSS
中有几个属性值关键字: initial
、inherit
、unset
、revert
, 用法和区别如下
inherit 用于继承父元素的属性值
initial 用于重置属性为W3C标准默认值
unset 清除掉浏览器的默认样式, 能使用继承值就使用继承值, 否则使用W3C标准默认值
revert 恢复浏览器的默认样式
# 九、动画
# 滑块动画
纯CSS实现滑块拖动动画, 利用animation
的delay
和paused
两个关键属性
源码已经上传到作者的Demos仓库-滑块动画 (opens new window)
<style>
.ball {
--delay: 0s;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #f00;
margin-bottom: 50px;
animation: move linear forwards 1s paused;
animation-delay: var(--delay);
}
@keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(100px);
}
}
</style>
<div class="container">
<!-- 小球 -->
<div class="ball"></div>
<!-- 滑块 -->
<input type="range" min="0" max="1" step="0.01" />
</div>
<script>
const input = document.querySelector('input')
const ball = document.querySelector('.ball')
const calc = () => {
ball.style.setProperty('--delay', `-${input.value}s`)
}
input.oninput = calc
calc()
</script>
# FLIP动画
所谓FLIP
动画, 也就是动画的四个步骤, First
、Last
、Invert
、Play
的缩写, 也就是先记录元素的初始状态, 然后记录元素的最终状态, 然后将最终状态反转到初始状态, 最后播放动画, 从而实现动画效果
First
记录要监控的元素位置Last
记录元素结构变化后的位置Invert
移动元素到First的位置Play
使用动画还原元素到本来的位置
源码已经上传到作者的Demos仓库-FLIP动画 (opens new window)
<style>
ul {
padding: 20px;
border: 1px solid #a12d02;
width: fit-content;
}
li {
list-style: none;
height: 30px;
width: 200px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
background-color: aquamarine;
margin: 10px 0;
}
</style>
<button class="first-begin">第一个元素播放FLIP</button>
<button class="full-sort-begin">全体元素排序播放FLIP</button>
<ul class="list">
<li class="list-item" style="background:#e75723; border-color:#a12d02" order="7">7. HTML + CSS</li>
<li class="list-item" order="6">6. Javascript</li>
<li class="list-item" order="1">1. 网络</li>
<li class="list-item" order="3">3. 工程化</li>
<li class="list-item" order="5">5. 框架</li>
<li class="list-item" order="4">4. 移动端</li>
<li class="list-item" order="2">2. Nodejs</li>
</ul>
<script>
// 公共元素和函数
const list = document.querySelector('.list')
function getLocation(elem) {
const rect = elem.getBoundingClientRect()
return rect.top
}
function raf(cb) {
requestAnimationFrame(cb)
}
</script>
<script>
// 处理第一个元素的FLIP动画
~function handleFirstFLIP() {
const btn = document.querySelector('.first-begin')
const firstItem = document.querySelector('.list-item:first-child');
const lastItem = document.querySelector('.list-item:last-child');
const start = getLocation(firstItem)
console.log('first', start);
btn.onclick = () => {
// 该行操作并不会立即绘制到浏览器, 而是等待下一个事件循环进行绘制
list.insertBefore(firstItem, null)
const end = getLocation(firstItem)
console.log('end', end);
const distance = start - end
firstItem.style.transform = `translateY(${distance}px)`
console.log('invert', distance)
raf(() => {
firstItem.style.transition = `transform 1s`
firstItem.style.removeProperty('transform')
console.log('play')
})
}
}()
</script>
<script>
// 实现全体元素的FLIP排序动画
const beginBtn = document.querySelector('.full-sort-begin')
beginBtn.onclick = () => {
const map = Array.from(list.children).reduce((o, c, n) => {
const index = c.getAttribute('order')
o[index] = {
order: index,
el: c,
start: getLocation(c),
end: -1,
dis: 0
}
return o
}, {})
const orderList = Object.values(map).sort((a, b) => b.order - a.order)
let prev = null
orderList.forEach(item => {
list.insertBefore(item.el, prev)
prev = item.el
item.end = getLocation(item.el)
item.dis = item.start - item.end
item.el.style.transform = `translateY(${item.dis}px)`
raf(() => {
item.el.style.transition = `transform 1s`
item.el.style.removeProperty('transform')
})
})
}
</script>
# 十、案例
该部分记录一些CSS
有意思的小案例
# 自动高度
有时候, 我们想在包含块的元素内部, 做一个自动高度的分界线。它的高度随着包含块的变化而自动调整,我们很容易就写出以下代码
最后发现, 作为分界线的子元素内部没有内容撑开, 导致它的实际高度为0
为了能够让该分界线能随着包含块元素撑开, 此时需要给该子元素添加min-height: inherit
::: code-group
.parent {
width: 80%;
min-height: 100px;
background: rgb(128, 128, 128, 0.3);
display: flex;
}
.left,
.right {
height: 100%;
width: 45%;
padding: 4px;
flex: 1;
}
.border {
width: 2px;
height: 100%; /* [!code --] */
min-height: inherit; /* [!code ++] */
background: red;
}
<link rel="stylesheet" href="./style.css">
<div class="parent">
<div class="left">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim facere iure placeat tempora
natus quasi cumque doloremque, maxime libero porro inventore vel distinctio earum commodi sunt veniam debitis,
doloribus labore!
</div>
<!-- 边界线, 无内容 -->
<div class="border"></div>
<div class="left">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim facere iure placeat tempora
natus quasi cumque doloremque, maxime libero porro inventore vel distinctio earum commodi sunt veniam debitis,
doloribus labore!
</div>
</div>
# 十一、一些特殊属性
# transform
CSS
中的transform
用于旋转,缩放,倾斜或平移给定元素等变形
但值得注意的是, 并非所有的盒子都可以进行transform
的转换(通常行内级元素不能进行形变)。所以,transform
对行内级非替换元素是无效的,比如span
、a
和table
中的一些元素等