说说css盒模型
浏览器在渲染html元素的时候,会把所有元素表示为一个一个矩形的盒子。
盒模型组成
content
即实际内容,显示文本和图像
padding
即内边距,内边距是透明的,取值不能为负,背景图片可以扩展到padding。
border
即边框,由粗细、样式、颜色三部分组成。例如
border:1px solid blackmargin
即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域。
盒模型分类
w3c标准盒模型(content-box)
width/height的范围只包括content,虽说是标准盒模型,怎么感觉没有ie盒模型好用捏。
ie盒模型(border-box),也叫做怪异盒模型
width/height的范围包括content, border, padding。
1 | box-sizing: content-box|border-box|inherit;//指定元素使用哪种盒模型来渲染,inherit表示继承父元素盒模型 |
说说em/px/rem/vh/vw区别
em
是一个相对单位,和rem相同的是,相对的都是字体的大小,只不过em相对的是父元素字体的大小,rem相对的是根元素html中字体的大小。
px
1px即1个css像素,是绝对单位。
rem
是一个相对单位,相对的是根元素**html的字体大小**。
如果想要简化font-size的转化,我们可以修改html的字体大小。
1 | html { font-size: 10px } |
vw/vh
把视口的宽高划分为100等份,1vw表示视口宽度的1/100,1vh表示视口高度的1/100,是一个相对单位,相对的是视口的宽/高,而百分比布局相对的是父元素的宽/高。
这里的视口,在PC端指的就是浏览器窗口,在移动端指的就是布局视口,一般就是设备屏幕。
1 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> #指定布局视口宽度为设备宽度 |
vmax/vmin:1vmax指的是1vw和1vh中较大的那个,反之1vmin指的是较小的那个。
设备像素、css像素、设备独立像素、dpr、ppi 之间的区别
css像素
css代码中的像素,一般1px就是一个css像素。
设备像素
设备像素(device pixels),又称为物理像素,指设备能控制显示的最小物理单位,就是显示屏上的一个一个像素点,是固定不变的。
屏幕的分辨率通常为a × b的格式,分别指的是宽,高上的物理像素点的个数。
设备独立像素
设备独立像素指的是与设备无关的逻辑像素,通过程序控制使用的虚拟像素,一个设备独立像素可能会对应多个设备像素。
在屏幕未缩放情况下(100%),1css像素=1设备独立像素,所以一般情况我们认为,css像素就是设备独立像素
我们也可以推断出缩放屏幕,其实就是在改变css像素与设备独立像素的比例关系
dpr
dpr = 设备像素/设备独立像素,在PC端,dpr = 1,在移动端,dpr>=1
我们通常讨论的是不缩放的情况下,也就是1css像素就是1设备独立像素,因此dpr又可以被认等于设备像素/css像素。
当设备像素比为1:1时,使用1(1×1)个设备像素显示1个CSS像素
当设备像素比为2:1时,使用4(2×2)个设备像素显示1个CSS像素
当设备像素比为3:1时,使用9(3×3)个设备像素显示1个CSS像素
PPI
即pixel per inch每英寸像素点的个数,这个PPI越大,说明物理像素点的密度更大,像素点一般也更多,像素点大小也越小,能更细致的展现图像。

比如,iPhone 3GS 和 iPhone 4/4s 的尺寸都是 3.5 寸,但 iPhone 3GS 的分辨率是 320x480,iPhone 4/4s 的分辨率是 640x960
这意味着,iPhone 3GS 在宽上有320个物理像素,iPhone 4/4s 在宽上有 640 个物理像素,很明显,在屏幕大小相同的情况下iPhone 4/4s有更多的像素,也就是说iPhone 4/4s的PPI更大。
空白问题
如果我们按照真实的物理像素进行布局,比如说我们按照 320 物理像素进行布局,到了 640 物理像素的手机上就会有一半的空白,为了避免这种问题,就出现了设备逻辑像素
我们统一 iPhone 3GS 和 iPhone 4/4s 都是 320 个虚拟像素,只是在 iPhone 3GS 上,最终 1 个虚拟像素换算成 1 个物理像素,在 iphone 4s 中,**1 个虚拟像素最终换算成 2 个物理像素(DPR=2)**。
模糊问题
我们思考一下,为什么原本能用1个物理像素表示的,为了解决空白问题,就硬要用2个物理像素表示呢,就非得让屏幕中的所有像素点参与展示吗,这样不就等同于放大图片吗,放大图片不是会变模糊吗,因为图片本身没有这么多细节(分辨率<实际用来渲染的像素数目)。所以对于PPI更高的设备,我们要准备分辨率更高的图片,才能完美的解决空白问题(一般来说,分辨率越高的图片,占用的物理像素点越多,文件大小越大,和屏幕分辨率是一个概念)
举个例子,我们有50x50分辨率的图片,我们通过css把它的大小设置为宽高都为50px:
- 放到iPhone 3GS上和PC端上(DPR=1),会占用50x50像素(物理像素),刚好等于图片的分辨率,所以不会模糊;
- 放到iPhone4/4s上(DPR=2),会占用100*100像素(物理像素),因为图片分辨率 < 实际参与渲染的物理像素,所以会变模糊。
如果我们不指定这张图片的大小,它在任何设备上都会占用实际的50*50个物理像素,但是由于不同设备的像素密度不同,所以图片看起来的大小也不同,所以会出现空白的问题。
如何实现元素隐藏
display:none
- 能实现元素隐藏,而且元素不再占有原来的位置
- 会触发页面的重排与重绘
- 元素对应的
dom对象仍然存在,无法再响应点击事件
visibility:hidden
- 能实现元素隐藏,而且元素占有原来的位置
- 只会触发页面的重绘
- 元素对应的
dom对象仍然存在,无法再响应点击事件
opacity:0
- 能实现元素隐藏,而且元素占有原来的位置
- 元素对应的
dom对象仍然存在,且能响应点击事件 - 一般情况下也会引发重绘
总结
这三种方法都能实现元素隐藏,display:none会让元素不再占有原来的位置,而其他两种则会保留原来的位置。
添加了display:none与visibility:hidden的元素都无法再响应点击事件,而添加了opacity:0的元素可以。
谈谈你对BFC的理解
什么是BFC
也叫块级格式化上下文,可以理解为css中的一种属性,开启了
bfc的盒子,被视为一块独立的渲染区域BFC盒子内部的元素不会影响外部元素的布局,外部元素也不会影响内部元素的布局,这意味着在一个BFC中的元素,不会与该BFC之外的浮动元素重叠,并且它们之间的外边距也不会折叠。
如何开启BFC
html标签默认开启了BFCoverflow的值不为visible(默认值)的元素,比如我们经常使用overflow:hidden来清除浮动,本质就是开启了BFC。- 添加了绝对定位,固定定位的元素
- 开启了浮动的元素
- 开启了flex布局或者grid布局的元素,这2种布局的内部元素布局方式确实独特,可以理解为开启了一块独立的渲染区域。
开启了BFC有什么作用
可以用来清除浮动
BFC盒子内部的浮动元素,也参与BFC盒子高度的计算,或者说BFC盒子会完全包裹内部的所有元素,包括浮动元素
所以浮动元素,绝对定位元素,固定定位元素,不需要担心其内部的浮动元素不参与自身高度的计算 。
防止被浮动元素压住
这一点体现了布局互不影响的特点,比如浮动元素不会被浮动元素压住。
可以用来解决边距塌陷问题
如果两个相邻的盒子存在边距塌陷问题,只要让其中一个盒子包裹一个开启了BFC的盒子,就能解决边距塌陷问题。
举例说明:
1 | <div class="box"> |
1 | .box { |
此时显示的图像如下:

当给A盒子添加浮动,我们期望A盒子会压住B盒子,所以应该只有红色,但是由于添加了浮动的盒子的宽度,宽度不再默认扩展至父元素宽度,所以A盒子的宽度为0,我们只能看到B盒子(蓝色)

如是我们给A盒子添加宽度100px,就出现了如下效果:

通过开发者工具检查发现,B盒子的宽度是300px,占满了父盒子,A盒子确实压住了B盒子,
前面介绍到开启BFC的盒子不会被浮动的元素压住,所以我们给B盒子添加overflow:hidden,但是发现效果没有任何改变,B盒子看起来还是被A盒子压住了,但是当我们打开开发者工具,我们可以发现,B盒子的宽度变为了200px,并没有被A盒子压住,因为我们没有指定B盒子的宽度,所以B盒子为了不被A盒子压住,就减少了自己的宽度。
为了让效果明显,我们设定B盒子的宽度为100px,就可以发现,B盒子未开启BFC时,会被A盒子压住,开启了则不会。

介绍一下flex弹性布局
开启Flex布局的元素(display:flex),称为flex容器container,其实也开启了BFC,它的所有子元素自动成为容器成员,称为flex项目item。
容器属性
包括flex-direction,flex-wrap,flex-flow,justify-content,align-items,align-content。
flex-direction
的方向决定了项目的排列方向
1 | .container { |
- row(默认值):主轴为水平方向,起点在左端,从左到右摆放
- row-reverse:主轴为水平方向,起点在右端,从右到左摆放
- column:主轴为垂直方向,起点在上沿。从上到下摆放
- column-reverse:主轴为垂直方向,起点在下沿,从下到上摆放
既然主轴的方向可以通过flex-direction来控制,哪侧轴方向呢?貌似没有哪个属性用来指定侧轴的方向,其实我们可以认为,主轴的方向确定了,侧轴的方向也确定了,且默认是从左到右或者从上到下。
flex-wrap
用来决定子元素一行放不下了是否换行
1 | .container { |
- nowrap(默认值):默认不换行
- wrap:换行,第一行在下方
- wrap-reverse:换行,第一行在上方,有点逆天了。
flex-flow
是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
1 | .box { |
justify-content
定义了项目(子元素)在主轴上的对齐方式
1 | .box { |
flex-start(默认值):向开始处(start)对齐
flex-end:向结束处(end)对齐
center:居中对齐
space-between:两端对齐,项目之间的间隔都相等
space-around:两个项目两侧间隔相等
1 | <div class="box"> |

align-items
定义子元素如何在侧轴上对齐(垂直于主轴),只适用于只有一根主轴线的时候(如果主轴为row,就是只有一行)
1 | .box { |
- flex-start:交叉轴的起点对齐
- flex-end:交叉轴的终点对齐
- center:交叉轴的中点对齐, 就是居中对齐
- stretch(默认值,拉伸):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content
定义多根主轴的对齐方式。如果项目只有一根主轴线(比如主轴是row的时候,只有一行子元素),该属性不起作用,所以说align-items属性和align-content属性不能同时使用。
1 | .box { |
- flex-start:与交叉轴的起点对齐
- flex-end:与交叉轴的终点对齐
- center:与交叉轴的中点对齐
- space-between:与交叉轴两端对齐,轴线之间的间隔平均分布
- space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍
- stretch(默认值):轴线占满整个侧轴,
align-content和align-items的默认值竟然都是stretch
可以看到,这些属性值和justify-content和align-items的属性值是几乎一样的,如果主轴有多根,可以把每根主轴当作侧轴上的一个元素。
容器成员属性
order
定义项目的排列顺序。数值越小,排列越靠前,默认为0,不指定order属性,那就是order:0
默认情况,子元素的排列顺序是在html文档中的书写顺序,而指定order能改变这个顺序。
1 | .item { |
flex-grow
定义了子元素的放大比例,默认为0,当容器设为flex-wrap: nowrap(默认值);即不换行的时候,如果一行没有占满:
flow-grow的值为0,如果一行没有占满,也不放大。- 子元素
flex-grow属性都为1,则它们将等分剩余空间。
flex-shrink
定义了子元素的缩小比例(容器宽度<子元素总宽度,且不换行时如何收缩),默认为1
如果所有子元素的flex-shrink属性都为1,当空间不足时,都将等比例缩小
如果一个子元素的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
1 | .box { |

可以看到即便子元素指定了宽度,如果一行放不下,也会等比例缩小。我们可以通过修改flex-shrink属性来修改具体的缩小规则。
flex-basis
设置的是元素在主轴上的初始尺寸,所谓的初始尺寸,就是元素在flex-grow和flex-shrink生效前的尺寸。
- auto: 默认值,这意味着项目将根据其内容,确定其初始大小,如设置了
width/height则元素尺寸由width/height决定 - 固定值:你可以指定一个具体的长度单位,如像素 (
px)、百分比 (%) 等。这会设置项目的初始大小,不考虑内容的自然尺寸。 - 0:完全依赖于
flex-grow来分配多余的空间。这种方式常用于创建等分布局,如果令一个元素的flex-basis:0,由于flex-grow默认为0,此时无论是否指定宽度,在主轴上的宽度都为0。
flex
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto,也是比较难懂的一个复合属性。
我们通常的书写方式flex:1修改的是flex-grow的值,即规定了分割剩余空间时的权重或者份数。
align-self
允许侧轴上的单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性,但是不能覆盖align-content的,
默认值为auto,表示继承父元素的align-items属性。
1 | .item { |

介绍一下grid网格布局
通过display:grid/inline-grid就能开启网格布局
1 | <div class="container"> |
上述代码实例中,.container元素就是网格布局容器,.item元素就是网格的项目,由于网格元素只能是容器的顶层子元素,所以p元素并不是网格元素
同样,Grid 布局属性可以分为两大类:
- 容器属性
- 项目属性
容器属性
display
display:grid 则该容器是一个块级元素
display: inline-grid 则容器元素为行内元素
grid-template-columns/grid-template-rows
grid-template-columns 属性设置列宽,grid-template-rows 属性设置行高
1 | .wrapper { |
以上表示固定列宽为 200px,行高为 50px
上述代码可以看到重复写单元格宽高,通过使用repeat()函数,可以简写重复的值,第一个参数是重复的次数,第二个参数是重复的值
所以上述代码可以简写成
1 | .wrapper { |
除了上述的repeat关键字,还有:
auto-fill:示自动填充,让一行(或者一列)中尽可能的容纳更多的单元格,只能在repeat函数中使用,用来替代具体的重复次数,而不是宽度或者高度
1 | //表示列宽是 200 px,但列的数量是不固定的,只要浏览器能够容纳得下,就可以放置更多的列 |
fr:片段,为了方便表示比例关系,非常类似flex布局中的份数,用份数来替代具体的宽高。要注意的是1fr不可写成fr,否则会出错。
1 | //表示第一个列宽设置为 200px,后面剩余的宽度分为两部分,宽度分别为剩余宽度的 1/3 和 2/3 |
minmax:产生一个长度范围,表示长度就在这个范围之中都可以应用到网格项目中。第一个参数就是最小值,第二个参数就是最大值,用一个范围来替代具体的宽高。
minmax(100px, 1fr)表示列宽不小于(>=)100px,不大于(<=)1fr
1 | grid-template-columns:repeat(auto-fill, minmax(260px,1fr)) |
auto:由浏览器自己决定长度
1 | //表示第一第三列为 100px,中间由浏览器决定长度 |
gap:定义宫格之间的间隔,还可指定row-gap和column-gap
容器成员属性
指定一个子元素占多少行多少列
1 | .carousel { |
CSS如何画一个三角形
可以通过指定边框(border)样式实现。
1 | .border { |
这样一个盒子的样式如下:

当盒子的宽高不断减小,那每个边框是不是就会变成一个三角形呢?

这样会得到四个三角形,但是我们只要一个,我们可以把其他边框的颜色变透明,但是这样隐藏的部分仍然占据部分高度,
比如我们只要下面的三角形,那我们就可以让上边框的的宽度变为0,效果就是这样。

然后再让左右边框变为透明的就好了。
1 | .border { |

如何实现元素水平居中,垂直居中
水平居中
行内元素/行内块元素
给父元素添加text-align:center
给父元素添加flex布局
1 | display: flex; |
给自身添加绝对定位
1 | position: absolute; |
块级元素
添加margin: 0 auto,要求块级元素的宽度确定
给父元素添加flex布局
1 | display: flex; |
给自身添加绝对定位
1 | position: absolute; |
垂直居中
行内元素
指定行内元素的行高line-height为父元素高度。
给父元素添加flex布局
1 | display: flex; |
给自身添加绝对定位
1 | position: absolute; |
行内块元素/块级元素
给父元素添加flex布局
1 | display: flex; |
给自身添加绝对定位
1 | position: absolute; |
总结
绝对定位和flex布局是万能的。margin:0 auto,text-align:center,line-height:父元素高度,是一些特殊情况。
说说对CSS预处理语言的理解?有哪些区别?
背景
CSS代码看起来是没有逻辑性的,不方便维护及扩展,不利于复用。css预处理语言在css的基础上,添加了变量,混入,嵌套等功能,让css代码看起来更有逻辑性,更容易维护和复用。
css预处理语言包括一套自定义的语法和一个解析器,解析器会把用自定义语法编写的代码转化成css代码。
常见的css预编译语言
sass
2007 年诞生,最早也是最成熟的 Css预处理器,目前受 LESS 影响,已经进化到了全面兼容 Css 的 Scss
sass和scss的区别与联系
是同一种css预处理语言(sass)的两种不同语法形式,它们都扩展了标准的 CSS,虽然有不同的自定义语法,但是使用同一个解析器来处理。sass使用缩进来表示代码块,不使用大括号 {} 和分号 ;更接近 Python 的风格
scss与 CSS 的语法几乎完全相同,所以更容易被现有开发者接受和使用
less
2009年出现,受SASS的影响较大,但又使用 Css 的语法,让大部分开发者和设计师更容易上手。
常用特性
嵌套
二者的嵌套语法都是一致的,甚至连引用父级选择器的标记 & 也相同
1 | .a { |
变量
变量的出现有效的提高了css代码复用性,减少了不必要的硬编码。
less声明的变量必须以@开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开
1 | @red: #c00; |
sass声明的变量跟less十分的相似,只是变量名前面使用$开头
1 | $red: #c00; |
作用域
在css预处理语言中,变量是具有作用域的。
sass中所有定义的变量都在全局作用域中,没有局部作用域。
1 | $color: black; |
编译后
1 | .scoped { |
所以,在sass中最好不要定义相同的变量名,无论定义在哪个地方,后面定义的会覆盖前面定义的。
less的变量作用域跟javascript十分的相似,有局部变量和全局变量之分。
1 | @color: black; |
编译后:
1 | .scoped { |
混入
Mixins可以将一部分样式抽出,作为单独定义的模块,被很多选择器重复使用,可以说是css预处理语言的精髓所在。
在less中的用法
1 | .alert { |
编译后
1 | .alert { |
Sass声明mixins时需要使用@mixin,后面紧跟mixin的名,也可以设置参数,参数名为变量$声明的形式
1 | @mixin large-text { |
代码模块化
模块化就是将复杂的Css代码按某种规则划分为一个个文件,每个文件就是一个模块,模块可以通过@import引入
scss、less二者的使用方法都如下所示。
1 | @import './common'; |
在原生css代码中,也能使用@import关键字导入其他css文件(无论是在css文件中还是在style标签中);url()是可加可不加的,要注意的是,结尾都要加上分号。
1 | @import url('./common'); |
如何实现单行/多行文本溢出的省略样式?
单行文本溢出显示

多行文本溢出显示
| 属性 | 作用 |
|---|---|
display: -webkit-box; | 将元素设为弹性盒子(旧版 WebKit Flexbox),是 -webkit-line-clamp 的前提 |
-webkit-box-orient: vertical(垂直); | 设置盒子的子元素垂直排列(这里是文本行) |
-webkit-line-clamp: 2; | ⭐ 关键!限制最多显示 2 行,超出则截断 |
overflow: hidden; | 隐藏被截断的内容(否则会溢出) |
text-overflow: ellipsis; | 超出部分用 ... 省略号表示 |
line-break: anywhere; | 允许在任意位置断行(避免长单词溢出) |
word-break: break-word; / break-all; | 控制单词断行方式(辅助换行) |
如何从CSS入手提高页面性能
浏览器有一个固定的渲染流程——只有在布局(layout)完成后,才能绘制(paint)页面,而布局的前提是要生成渲染树(render tree),而渲染树的生成则需要 DOM树 和 CSSOM 树的配合。
如果先让用户看到一个没有样式的页面,等 CSS 样式解析完后再重绘(repaint),这样的体验会很差。所以,浏览器会等到DOM树构建完毕,且首屏需要的CSS加载解析完毕的时后,才开始渲染。
内联首屏关键css
将提取出的关键 CSS ,直接嵌入到 HTML 文件的 <head> 部分,使用 <style> 标签包裹。
内联css代码将成为html文件的一部分,会随着html文件的下载而被下载,能够使浏览器在下载完html后就能立刻渲染,
而如果使用link标签引用css代码,在解析html结构过程中,遇到link标签,才会开始下载css代码,再解析,再渲染。
所以,内联CSS省去了加载css文件的时间,从而提高了首屏加载速度。
虽然内联关键 CSS 可以加速首屏渲染,但过大的内联样式可能会增加 HTML 文件的大小,反而影响加载速度。因此,应该尽量保持内联 CSS 的精简。
注意:内联的css样式解析完毕后会立即渲染(如果DOM也构建完毕的话),不管是否存在link标签引入的其他css文件,所以可能存在样式闪烁问题。
资源压缩
将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间,这一点应该是比较好理解的。
合理使用css选择器
css匹配的规则是从右往左开始匹配,例如#markdown .content h3匹配规则如下:
- 先找到h3标签元素
- 然后去除祖先不是.content的元素
- 最后去除祖先不是#markdown的元素
如果嵌套的层级更多,页面中的元素更多,那么匹配所要花费的时间代价自然更高。
所以最好不要嵌套使用过多复杂选择器。
不使用@import
css样式文件有两种引入方式,一种是link元素,另一种是@import
使用 @import 规则引入的 CSS 文件是串行加载的,打开开发者工具->网络,即可观察到这一现象。

1 | /* styles.css */ |
在这个例子中,reset.css、layout.css 和 theme.css 是按顺序加载的,只有当前一个文件加载完成后才会开始加载下一个。如果这些文件都是渲染首屏需要的,那么这样导入css文件将会减缓首屏渲染速度。
1 | <head> |
尽管 <link> 标签默认会阻塞页面渲染,但它允许浏览器并行加载多个 CSS 文件。这意味着虽然整个页面的渲染仍然会被延迟,但单个文件的加载,不会影响其他文件的下载直到所有这些文件都解析完毕。
说说css的层叠顺序
层叠上下文
有层叠上下文的元素,一般比普通元素(未开启定位的元素)层级高
如何产生层叠上下文
- html元素默认有层叠上下文,称为“根层叠上下文”
- 普通元素设置
position属性为非static值,并设置z-index属性为具体数值(不为auto),产生层叠上下文。
如何查找
从父元素开始向上查找,直至查找到一个有层叠上下文的元素。
z-index
静态布局元素(postion:static)的z-index属性不会生效,只对定位元素有效。
默认值为auto,不会产生层叠上下文。
父元素的z-index权重可以理解为比子元素高(老爸比不过对面的,自己也就比不过),我们一般只在同一个层叠上下文中比较层叠优先级。
层叠顺序
父背景 < 负 z-index 子上下文 < 块级非定位 < 浮动 < 行内非定位 < 定位 (auto/0) < 正 z-index 子上下文。
由此可知,浮动盒子的层叠优先级低于行内/行内块元素,但高于块级元素,所以浮动盒子不会压住后面的行内/行内块元素,会压住后面的块级元素。
开启了定位的元素(相对定位,绝对定位,固定定位)的,即便不指定z-index,层叠优先级也比行内/行内块元素高,当然也就比浮动元素高。如果指定了z-index为负值,优先级反而变的比它们低了。
怎么理解回流跟重绘?什么场景下会触发?
是什么
在HTML中,每个元素都会被表示成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:
- 回流:布局引擎会根据各种样式,计算每个盒子在页面上的大小与位置,简单的来说,回流的作用就是布局元素,即确定元素的位置和大小
- 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制,简单的来说,就是更为细致的渲染盒子。
具体的浏览器解析渲染机制如下所示:

- 解析
HTML,生成DOM树,解析CSS,生成CSSOM树 - 将
DOM树和CSSOM树结合,生成渲染树(Render Tree),生成了渲染树才能确定每个元素的大小和布局方式,才能进行布局(layout) - Layout(回流):根据生成的
渲染树,进行回流(Layout),得到节点的几何信息(位置,大小) - Painting(重绘):根据
渲染树以及回流得到的几何信息,得到节点的绝对像素 - Display:将像素发送给GPU,展示在页面上
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变
当我们对 DOM 的修改引发了 DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来,这里就发生了回流
当我们对 DOM的修改导致了样式的变化(color或background-color),却并未影响其几何属性时,浏览器直接为该元素绘制新的样式,这里就仅仅触发了重绘。
如何触发回流
- 添加或删除
可见的DOM元素 - 元素的
位置发生变化 - 元素的
大小发生变化 - 页面初次渲染时(这避免不了)
- 浏览器的
窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的,这个依据变了,就难免要重新计算) - 获取元素以
client,scroll,offset开头的布局属性,这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流。
如何触发重绘
触发回流一定会触发重绘,除此之外还有一些其他引起重绘行为:颜色的修改,文本方向的修改,阴影的修改
总的来说,如果修改了css样式,没触发回流,那就只会触发重绘。
浏览器的优化机制
由于每次回流都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化回流过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。
当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据,因此浏览器不得不立即清空队列,触发回流重绘来返回正确的值。
编码过程中该如何优化
离线操作
通过设置元素属性display: none,将其从页面上去掉(完全从渲染树中移除),然后再进行后续操作,这些后续操作也不会触发回流与重绘,当设置display:block时,浏览器会重新计算所有累积的样式修改,触发全局回流和重绘。
避免多次访问布局属性
因为访问布局属性会强制触发回流
css选择器有哪些?优先级?哪些属性可以继承?
选择器
CSS选择器是CSS规则的第一部分。就是选择符合匹配规则的元素,给它们应用相应的样式规则。
关于css属性选择器常用的有:
id选择器(#box),选择id为box的元素,id选择器选择唯一的元素,每个元素的id应该是唯一的。
类选择器(.one),选择类名为one的所有元素
标签选择器(div),选择标签为div的所有元素
属性选择器:
1
2[attribute] 选择带有attribute属性的元素
[attribute=value] 选择所有使用attribute=value的元素复合选择器
后代选择器(#box div),选择id为box元素内部所有的div元素,只要是box后代div元素就行。
1
a.className a
这是一个有效的选择器。它会选择带有类名className的a标签之下的另一个
a标签。但是从html角度考虑,a标签不能套接a标签,而且它的权重比.className a要高。子元素选择器(.one > .one_1),选择父元素为.one的所有.one_1的元素
群组选择器(div,p),选择div、p的所有元素,这两类元素使用相同的css规则。
还有一些使用频率相对没那么多的选择器:
伪类选择器:最经典的是标志就是只有一个冒号
1
2
3
4
5:link :选择未被访问的链接,常见的使用场景是和a标签绑定的,即a:link
:visited:选取已被访问的链接,常见的使用场景是和a标签绑定的,即a:visited
:active:选择活动链接,就是点击a标签不松手时的样式。常见的使用场景是和a标签绑定的,即a:active
:hover :鼠标指针浮动在上面的元素
:focus :选择具有焦点的注意:上述与a标签有关的书写顺序应该是,a:link->a:hover->a:visited->a:active,书写顺序越靠后,优先级越高,这样书写才能保证相应的样式会如期生效。
1
:nth-child(n)、:first-child、:last-child 等结构伪类选择器(也是伪类选择器):基于元素在其父级中的位置选择元素。
:nth-child(n)和:nth-of-type(n)的区别
:nth-child(n)是先根据顺序查找元素,再匹配类型。:nth-of-type(n)是先根据元素类型筛选元素,再根据顺序查找元素。简单的来说一个是先查找再匹配,一个是先筛选再查找。
1
2
3
4
5
6
7<div class="container">
<h2>标题</h2>
<p>段落一。</p>
<p>段落二。</p>
<h2>另一个标题</h2>
<p>段落三。</p>
</div>1
2
3.container :nth-child(3) {//前面不加标签就代表选择第三个子元素,且不限制标签类型
background-color: yellow;
}选中的是 <p>段落二。</p>这个元素1
2
3.container p:nth-child(4) {//前面加上了一个p标签,就代表选择第四个元素,且必须是p标签
background-color: yellow;
}选中的是第四个元素,且要求它的类型是p,但是第四个元素类型是h2,所以这个css规则不会生效1
2
3.container p:nth-of-type(3) {//:nth-of-type前面必须加标签,限制类型
background-color: lightblue;
}//选中的是内部所有的p元素中的第三个,也就是第五个元素<p>段落三。</p>伪元素选择器
伪元素通常以双冒号
::开头,这是为了区分伪类选择器(以单冒号开头),但在一些较老的浏览器中,单冒号:也被支持。1
2::before : 选择器在 被选元素的内容 前面插入内容
::after : 选择器在 被选元素的内容 后面插入内容伪元素的好处就是可以在不改变
页面结构的前提下,往页面中插入内容。伪元素选择器还可以用来清除浮动
1
2
3
4
5.clearfix::after {
content: "";//指定要插入的具体的内容
display: block; //指定插入的伪元素的类型,设置为block让其独占一行
clear: both; //确保左右两边没有浮动元素,可以用用来清除浮动
}clear属性的解释:指明某个元素周围不能出现浮动元素,通常借助
伪元素来清除浮动,属性值为none | left | right | both所以说,周围最多是指的左右两边(both)。
优先级
单个选择器的优先级排序:内联 > ID选择器 > 类选择器(包括伪类,结构伪类)= 属性选择器 > 标签选择器 = 伪元素选择器
那复合选择器的优先级怎么比较呢?先比较复合选择器中包含的优先级最高的选择器,如果优先级相同,则比较优先级最高的选择器的数量,如果数量也相同,则比较次高优先级的选择器,依此类推。
经过上面的优先级计算规则,我们知道内联样式的优先级最高,如果外部样式需要覆盖内联样式,就需要使用!important
1 | .box{ |
继承属性
在css中,继承是指的是给父元素设置一些属性,后代元素会自动拥有这些属性
关于可继承的属性,可以分成:
字体系列属性
简单来说,就是font开头的属性,所有字体属性。
1 | font:组合字体 |
文本系列属性
1 | text-indent:文本缩进 |
元素可见性visibility
这就意味着,给一个父元素添加visibility:hidden,他的子元素也都会被隐藏。
1 | quotes:设置嵌套引用的引号类型 |
光标属性
1 | cursor:箭头可以变成需要的形状 |
继承中比较特殊的几点:
a标签的字体颜色不会继承父元素的字体颜色,它有默认的颜色h1-h6标签字体的大小也不会继承父元素的字体大小
无继承的属性
- display
- 文本属性:vertical-align、text-decoration
- 盒子模型的属性:宽度、高度、内外边距、边框等
- 背景属性:背景图片、颜色、位置等
- 定位属性:浮动、清除浮动、定位position等
- 生成内容属性:content、counter-reset、counter-increment
- 轮廓样式属性:outline-style、outline-width、outline-color、outline
- 页面样式属性:size、page-break-before、page-break-after
CSS3新增了那些特性
边框border-radius:创建圆角边框
值可以是绝对单位px也可以是百分比单位。
如果是绝对单位,比如为10px,则从盒模型每个角的顶点开始,往两条边的方向分别延伸10px,然后在这两个点的位置做一条垂直的边,确定交点,然后再以这个焦点为圆心做一个圆,然后在舍去这个圆截取的部分 ,就得到了圆角模型。
如果单位是百分比,则先转化为绝对单位,相对的是每个角对应的两条边。
1 | .box { |

图中圆的半径为10px
1 | .box { |

图中的红线只是辅助线,可以看到当令 border-radius: 50%时就能得到一个椭圆,如果盒子的宽高相等就是一个圆形。
50%是border-radius能取得的最大值,再增加也不会有效果。
box-shadow:为元素添加阴影
设置属性如下:
- 水平阴影(x轴)
- 垂直阴影(y轴)
- 模糊距离(z轴)
- 阴影尺寸(影子大小)
- 阴影颜色
- 内/外阴影
1 | box-shadow: 3px 3px 4px; |

如果第一个属性值(x)为正数,则阴影就会出现在盒模型的右边 ,值越大右边阴影越宽,反之则出现在盒模型的左边
如果第二个属性值(y)为正数,则阴影就会出现在盒模型的下边 ,值越大下边阴影越宽,反之则出现在盒模型的上边
如果第三个属性值(z)表示盒模型距离视口的距离,一般为正值,这个值增大可以看到盒模型仿佛在贴近屏幕,阴影就变越来越模糊了。
第四个值为阴影的尺寸,其实修改第一,二个属性的值就能控制阴影的大小了,这个值存在的意义就是在原有的大小上再添加。
transition过渡
transition属性可以为指定的一个或多个CSS属性添加过渡效果,让样式改变看起来更自然,多个属性之间用逗号进行分隔,必须规定两项内容:css属性和持续时间
1 | transition: CSS属性,花费时间,效果曲线(默认ease),延迟时间(默认0) |
详细介绍见下文
transform转换
transform属性允许你平移,缩放,旋转指定元素
详细介绍见下文
animation 动画
详细介绍见下文
渐变
linear-gradient:线性渐变
线性渐变创建了一条沿直线前进的颜色带,这条直线的方向,角度是可以自定义的。
基础线性渐变
1 | .simple-linear { |

改变渐变方向
默认情况下,线性渐变的方向是从上到下,你可以指定一个值来改变渐变的方向,我们把这个值放到第一个参数的位置,值的类型可以是关键字,也可以是具体的角度。
1 | .horizontal-gradient { |

1 | .diagonal-gradient { |

设置渐变角度
在使用角度的时候,0deg 代表渐变方向为从下到上,90deg 代表渐变方向为从左到右,诸如此类正角度都属于顺时针方向。而负角度意味着逆时针方向。
1 | .angled-gradient { |

控制渐变的进程
默认情况下,渐变在两个相邻的色标之间都是均匀推进的,两个色标之间的中点是颜色值的中点,也可以控制在哪个位置才开始均匀推进。
1 | .box{ |

如图所示,前50px的部分的颜色都是blue。
堆叠渐变
你可以将渐变与其他的渐变堆叠起来。只要顶部的渐变不是完全不透明的,那么下面的渐变就会依然可见。
顶部的渐变就是最先声明的渐变,就是书写位置最靠前的渐变。
1 | .a { |

其实多个背景图片也是按照这种规则来堆叠的,毕竟渐变色也属于background-image。
1 | .box { |
效果如图:

radial-gradient:径向渐变
径向渐变类似于线性渐变,除了是从一个中心点向外辐射的。你可以指定中心点的位置。你还可以使其为圆形或者是椭圆形。
更多内容参考:使用 CSS 渐变 - CSS:层叠样式表 | MDN
flex布局与grid布局
有文章介绍,不赘述。
CSS3新增动画
css实现动画的方式,有如下几种,都是css3新增的
- transition:实现渐变动画
- transform:转变动画
- animation:实现自定义动画
transition
transition的属性如下:
- property:填写需要变化的css属性,我们一般直接填写all
- duration:完成过渡效果需要的时间单位(s或者ms)
- timing-function:完成效果的速度曲线
- delay:动画效果的延迟触发时间
time-function的所有值如下

注意:虽然为了方便,我们经常使用all,表示给所有属性添加过渡,但并不是所有的属性都能使用过渡的,如display:none<->display:block,而visibility:hidden<->visibility:visible却可以,所以不是显示与隐藏的问题,就是display:none<->display:block不能。
transform
一般配合transition过渡使用,不影响其他盒子的位置。元素会在视觉上发生位置变化,但实际位置没变,这一点和相对定位相同。
注意的是,transform不支持inline元素(因为无法指定宽高(也无法指定垂直外边距)),使用前把它变成block
包含四个常用的功能:
translate:位移
1 | transform:translate(15px,15px) //向右向下平移15px |
传入的单位也可以是百分比,相对的是元素自身的宽/高。
rotate:旋转
1 | transform:rotate(45deg) |
正值表示为顺时针旋转45度
1 | transform-origin: x y |
设置平面转换中心点,默认是元素中心,x y可以是像素(px),也可以是百分比,相对的也是元素自身的宽高,比如transform-origin: 50% 50%;指定的转换中心点就是盒子中点。也可以是方位名词,所有组合方式只能表示9个点。
scale:缩放
1 | transform:scale(x,y) |
里面的数字不跟单位,就是倍数的意思,分别表示宽高缩放为原来的多少倍。
skew:倾斜
transform-origin
transform-origin 是 CSS 中的一个属性,用于定义应用变换(如旋转、缩放、倾斜等)的原点位置。默认情况下,变换的中心点是元素的中心(即 50% 50%)
值的数量
transform-origin 可以接受一到三个值。
如果指定一个值,就是在指定x轴的位置,Y 轴默认为 50%。
如果指定两个值,分别指定 X 轴和 Y 轴的位置。在支持 3D 转换的情况下,可以指定 X 轴、Y 轴和 Z 轴的位置。
可能的值
百分比:例如 0%, 50%, 100% 等。0% 0% 表示左上角,100% 100% 表示右下角。
关键字
- 水平方向:
left,center,right - 垂直方向:
top,center,bottom
长度单位:如 px, em, cm 等具体数值。
animation
先通过@keyframes定义动画,再通过animation属性使用动画
定义动画
CSS 动画只需要定义一些关键的帧,而其余的帧,浏览器会根据计时函数插值计算出来
通过 @keyframes 来定义关键帧
比如,如果我们想要让元素旋转一圈,只需要定义开始和结束两帧即可:
1 | @keyframes rotate{ |
from 表示最开始的那一帧,to 表示结束时的那一帧。rotate表示自定义的动画名。
也可以使用百分比刻画生命周期:
1 | @keyframes rotate{ |
使用动画
定义好动画,我们就可以直接使用了,使用动画涉及到了许多属性,记忆起来较为困难。
animation是其他8个属性的简写,当使用 animation 简写属性时,按顺序来书写值是很重要的。如果你省略了某些中间的属性,必须确保后续属性仍然按照正确的顺序给出。参考下面书写的顺序。
| 属性 | 描述 | 属性值 |
|---|---|---|
| animation-name | 指定 @keyframes 动画的名称 | |
| animation-duration | 指定动画完成一个周期所需要时间,单位秒(s)或毫秒(ms),默认是 0 | |
| animation-timing-function | 指定动画计时函数,即动画的速度曲线,默认是 “ease” | linear、ease、ease-in、ease-out、ease-in-out |
| animation-delay | 指定动画延迟时间,即动画何时开始,默认是 0 | |
| animation-iteration-count | 指定动画播放的次数,默认是 1 | 具体次数,infinite(无限多次) |
| animation-direction 指定动画播放的方向 | 默认是 normal | normal、reverse、alternate、alternate-reverse |
| animation-fill-mode | 定义动画在播放之前或之后如何影响元素的样式。默认是 none | forwards、backwards、both |
| animation-play-state | 指定动画播放状态,正在运行或暂停。默认是 running | running、pauser |
1 | .spinner { |
什么是响应式设计?响应式设计的基本原理是什么?
是什么
响应式设计的作用就是,让同一套前端代码,能根据设备屏幕的尺寸,主动调整布局和样式
如何实现
方法一:添加一个meta标签
在移动端实现响应式,必须添加一个name=viewport的meta标签,让布局视口等于设备宽度
1 | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”> |
width=device-width:布局视口的宽度等于设备宽度。maximum-scale:是缩放比例的最大值inital-scale:是缩放的初始化user-scalable:值为布尔值,决定用户是否可以进行缩放操作。
方法二:像素单位使用相对单位
百分比
height、width属性的百分比依托于父元素的宽高,但是其他属性则不完全依赖父元素。比如,子元素的top/left和bottom/right如果设置百分比,则相对于最近的定位元素的宽高(补充一下,dom元素的offsetTop和offsetLeft属性相对的也是最近的定位元素)子元素的内外边距(padding和margin)如果设置百分比,不论是垂直方向或者是水平方向,都相对于直接父元素的
width,而与父元素的height无关。如果 padding-top: 10% 相对于父元素高度,而父元素高度又由子元素内容撑开(比如子元素有 padding-top),就会形成“鸡生蛋还是蛋生鸡”的死循环。而宽度通常由父容器明确设定(或视口决定),作为基准更稳定。vw/vh:已介绍,不赘述
rem:已介绍,不赘述
方法三:使用flex布局,grid布局
方法四:媒体查询
媒体查询是css3中引入的功能,作用类似js中的if-else语句,通过媒体查询,可以通过给不同分辨率的设备,编写不同的样式来实现响应式的布局。即便我们使用了相对单位,也只能解决同一类型设备的响应式问题,比如在PC端设计的网页,即便使用了相对单位,在移动端展示的效果也是宽大于高的矩形,展示效果不佳,此时就需要借助媒体查询来解决这个问题。
基本语法
1 | @media [not|only] media-type and (media-feature) { |
@media:用于定义一个媒体查询。[not|only]:可选关键字,not排除特定条件,only用来指定仅在满足条件时应用样式。media-type:指定媒体类型,例如screen(屏幕)、print(打印机)等。如果省略,则默认为all,即适用于所有类型的媒体。and:逻辑运算符,用来连接多个媒体特性条件。(media-feature):媒体特性条件表达式,例如(max-width: 600px),注意不要写成(max-width=600px)。
示例
根据屏幕大小确定字体大小
1 | /* 当屏幕宽度小于等于600px时 */ |
媒体查询不仅可以使用在css代码中,还能在link标签中使用,比如下面的代码可转化成:
1 | // 这段代码无论写在哪里,max-width指的都是视口的宽度 |
1 | //其中的max-width指的是视口的宽度,或者说可见区域的宽度 |
1 | //test.css |
这样就能实现在不同尺寸的设备上,使用不同的样式表,.而实现响应式布局的效果。
