HTML与CSS常见面试题汇总
本文对HTML和CSS的常见面试题做了整理,并对一些常考的代码题提供了代码演示。
HTML
DOCTYPE
下面是一段比较常见的html5页面的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
</html>其中最顶部有<!DOCTYPE html>的描述,这时候页面采用标准模式渲染;如果不设置,页面则会触发兼容模式(怪异模式)。两者的具体区别如下:
标准模式: 使用最新的浏览器解析规则标准解析文档。
兼容模式: 为了保证老式浏览器的正常访问,通过向后兼容的方式来模拟老式浏览器的行为。
元素类型
有三种元素类型:
行内元素:
abspanimginputselectstrong;块级元素:
divulollidldtddh1h2h3h4h5h6p空元素:
<br>、<hr>、<img>、<input>、<link>、<meta>;
HTML5 的新特性
下面提几个常见的:
语义化标签
Storage API:localStorage、sessionStorageHistory API:pushState、replaceStateCanvas画布Web Worker音视频:
<video/>、<radio/>
src 和 href 的区别
首先介绍下两者的含义:
src是source的简写,通过src指向请求外部资源的来源地址,指向的内容会嵌入到文档中当前标签所在位置,目的是要把文件下载到html页面中去。href是Hypertext Reference的简写,表示超文本引用,指向网络资源所在位置。
其本质区别就是浏览器加载方式的不同:
浏览器遇到
href时会并行加载,即不会停止对文档的处理浏览器遇到
src时会阻塞,直到将该资源加载完毕
script 标签中 defer 和 async 的区别
如图:

script在没加其他标签之前,加载脚本会阻塞DOM的渲染。
defer和async都用于异步加载脚本,不过有以下的区别:
async加载完脚本后立即执行 (execution),因此可能会阻塞 DOM 解析;defer加载(fetch)完成后延迟到DOM解析完成后才会执行(execution),但会在事件DomContentLoaded之前执行
sessionStorage,localStorage 和 cookie
共性:
都是在客户端存储数据,不占用服务器的带宽和存储空间。
都是以键值对的形式存储数据。
都可以通过
JavaScript进行读取、写入、修改和删除。
区别:
存储容量:
cookie的存储容量最小,只有4KB左右;localStorage的存储容量一般为5MB到10MB;sessionStorage 的存储容量也为5MB到10MB。生命周期:
cookie可以设置过期时间,存在一定的时间后会自动失效;localStorage和sessionStorage没有过期时间,除非通过JavaScript手动删除。数据共享:
cookie可以跨域名共享,但是localStorage和sessionStorage只能在同一域名下共享。数据传输:
cookie在请求时会自动传输到服务器端,而localStorage和sessionStorage不会自动传输到服务器端。安全性:
cookie存储的数据会被浏览器自动发送到服务器端,存在一定的安全风险;而localStorage和sessionStorage存储的数据只存在于客户端,不会被自动发送到服务器端,相对更安全。
JSONP 的原理是什么
由于JSONP的实现原理是借助于script标签,所以把这个问题也放进HTML里。
本质就是创建一个script用于发请求,并使用指定的回调来接收数据。不过,它只能用于GET请求获取数据,接收到的数据也只能是一个字符串。
一个简单的jsonp实现:
function jsonp_simple({ url, onData, params }) {
const script = document.createElement("script");
// 一、默认 callback 函数为 padding
script.src = `${url}?${stringify({ callback: "padding", ...params })}`;
// 二、使用 onData 作为 window.padding 函数,接收数据
window["padding"] = onData;
// 三、动态加载脚本
document.body.appendChild(script);
}浏览器的渲染过程
以下是浏览器的渲染过程的一般流程:
解析
HTML代码:浏览器首先解析HTML代码,构建DOM树,将HTML代码转换成一个树形结构,每个HTML元素都是树种的一个节点。解析
CSS代码:浏览器接下来解析CSS代码,构建CSSOM树,将CSS代码转换成一个树形结构,每个CSS样式都是树种的一个节点。创建渲染树:浏览器将
DOM树和CSSOM树合并在一起,生成一棵渲染树。布局:浏览器计算每个元素在屏幕上的位置和大小,称之为布局(也称为回流、重排)。布局是一个计算密集型的过程,因此应该避免频繁的更改
DOM元素。绘制:浏览器根据渲染树和布局信息,将每个元素绘制在屏幕上,称之为绘制(也称为重绘)。
合成:浏览器将绘制好的元素合成到屏幕上,称之为合成。
比较流行的几种浏览器内核
| 浏览器内核 | 浏览器 |
|---|---|
| Blink | Google Chrome, Opera, Microsoft Edge(版本 79 及以后) |
| WebKit | Safari, Google Chrome(版本 27 及以前)、Opera(版本 15 及以前) |
| Gecko | Mozilla Firefox |
| Trident | Microsoft Internet Explorer, Microsoft Edge(版本 44 及以前) |
用户在浏览器地址栏输入URL点击访问时发生的过程
地址栏输入URL敲下回车后,浏览器会经历以下步骤:
DNS解析:浏览器首先会检查URL中的域名部分,将其转换为对应的IP地址。这个过程叫做DNS解析,浏览器会向本地DNS服务器或者ISP的DNS服务器发送DNS查询请求,查询域名对应的IP地址。建立
TCP连接:浏览器向服务器发起TCP连接请求。TCP是一种可靠的传输协议,它会保证数据能够稳定地到达目标服务器。发送
HTTP请求:浏览器向服务器发送HTTP请求报文,报文中包含请求方法、请求头、请求体等信息。请求方法一般为GET或POST,请求头会包含一些元数据,例如 User-Agent、Cookie、Referer等。接收
HTTP响应:服务器收到请求后,会向浏览器返回HTTP响应报文,报文中包含响应状态码、响应头、响应体等信息。响应状态码一般有200、301、302、404等,响应头会包含一些元数据,例如Content-Type、Content-Length等,响应体则是响应的具体内容。渲染页面:如果响应状态码为
200,浏览器就会开始解析响应体中的HTML、CSS和JavaScript代码,并根据这些代码渲染页面。具体渲染过程包括解析HTML标签、构建 DOM 树、计算样式、布局、绘制等步骤。断开连接:页面渲染完成后,浏览器会断开
TCP连接,释放资源,等待用户的下一次请求。
这个过程涉及到的知识点非常多,下面把其中涉及到的一部分知识点进行拆解:
DNS解析过程
DNS解析的具体过程如下:
- 检查本地缓存:先检查本地计算机是否有该域名对应的IP地址,如果有,则直接返回该
IP地址,不再进行后续查询过程。 - 发送请求到本地
DNS服务器:如果本地计算机中没有该域名对应的IP地址,则向本地DNS服务器发送查询请求。 - 本地
DNS服务器查询缓存:本地DNS服务器会先检查自己的缓存中是否有该域名对应的IP地址,如果有,则直接返回该IP地址。 - 本地
DNS服务器向根DNS服务器查询:如果本地DNS服务器中没有该域名对应的IP地址,则向根DNS服务器发送查询请求。 - 根
DNS服务器向顶级DNS服务器查询:根DNS服务器会返回一个顶级域的DNS服务器的IP地址,本地DNS服务器会向该顶级DNS服务器发送查询请求。 - 顶级
DNS服务器向权威DNS服务器查询:顶级DNS服务器会返回一个权威DNS服务器的IP地址,本地DNS服务器会向该权威DNS服务器发送查询请求。 - 权威
DNS服务器返回IP地址:权威DNS服务器中保存有该域名对应的IP地址,会将该IP地址返回给本地DNS服务器。 - 本地
DNS服务器将IP地址返回给计算机:本地DNS服务器会将该IP地址保存在缓存中,并将该IP地址返回给本地计算机,以便进行下一步的通信。
DNS解析使用的算法有两种:
迭代查询算法:本地
DNS服务器向根DNS服务器查询,得到顶级域的DNS服务器的IP地址,再向顶级域的DNS服务器查询,得到权威DNS服务器的IP地址,最终得到域名对应的IP地址。这种算法是逐级查询的过程,每一步都需要等待上一步返回结果后才能进行下一步查询。递归查询算法:本地
DNS服务器向根DNS服务器查询,得到顶级域的DNS服务器的IP地址,并向顶级域的DNS服务器查询,得到权威DNS服务器的IP地址,并将查询结果返回给本地DNS服务器。这种算法是本地DNS服务器向其他DNS服务器进行查询,并等待其他DNS服务器返回结果的过程,本地DNS服务器会一直等待直到得到查询结果。
TCP的三次握手和四次挥手
TCP连接的建立需要三次握手:
第一次握手:客户端向服务器发送
SYN报文,表示请求建立连接,并选择一个随机的初始序列号。第二次握手:服务器收到
SYN报文后,向客户端发送SYN+ACK报文,表示同意建立连接,并给出自己的随机序列号。第三次握手:客户端收到
SYN+ACK报文后,向服务器发送ACK报文,表示确认建立连接,并给出自己的序列号。此时,连接建立成功,双方开始传输数据。
上述每一次握手的作用如下:
- 第一次握手:客户端发送网络包,服务端收到了 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
- 第二次握手:服务端发包,客户端收到了 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常
- 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常
通过三次握手,就能确定双方的接收和发送能力是正常的。之后就可以正常通信了。
TCP连接断开,需要四次挥手:
第一次挥手:客户端向服务器发送
FIN报文,表示请求关闭连接。第二次挥手:服务器收到
FIN报文后,向客户端发送 ACK 报文,表示已经收到了客户端的请求。第三次挥手:服务器向客户端发送
FIN报文,表示自己的数据已经传输完毕,请求关闭连接。第四次挥手:客户端收到
FIN报文后,向服务器发送ACK报文,表示已经收到了服务器的请求。
四次挥手的设计是为了确保在数据传输结束后,双方的连接可以安全地关闭,释放资源。在挥手过程中,第一次挥手是客户端请求关闭连接,第二次挥手是服务器确认请求,第三次挥手是服务器请求关闭连接,第四次挥手是客户端确认请求。这样的设计可以防止双方同时发送请求、重复请求等问题,保证连接的安全性和可靠性。
常见的HTTP状态码
根据状态码的前缀进行分类,有如下几种:
1xx- Informational:请求已被服务器接收,继续处理。2xx- Success:请求已成功被服务器接收、理解、并接受。200- OK:请求成功201- Created:请求并创建了新的资源204- No Content:请求已成功处理,但没有返回响应正文。
3xx- Redirection:需要客户端采取进一步的操作才能完成请求。301- Moved Permanently:永久重定向,请求的URL已永久移动到新的位置302- Found:临时重定向,请求的资源已被分配了新的URI,希望用户本次能使用新的URI访问。304- Not Modified:表示客户端发送附带条件请求时,服务器端允许请求访问资源,但未满足条件的情况。(一般用于协商缓存校验:服务器收到请求,将服务器的中此文件的ETag,跟请求头中的If-None-Match相比较,如果值是一样的,说明缓存还是最新的,Web服务器将发送304 Not Modified响应码给客户端表示缓存未修改过,可以使用。)
4xx- Client Error:请求包含语法错误或无法完成请求。400- Bad Request:请求包含语法错误或无法完成请求。401- Unauthorized:请求需要身份验证,客户端需要提供凭据。403- Forbidden:服务器拒绝执行请求,客户端没有权限。404- Not Found:服务器无法找到请求的URL。
5xx- Server Error:服务器在处理请求时发生错误。500- Internal Server Error:服务器在处理请求时发生了错误。503- Service Unavailable:服务器当前无法处理请求,因为过载或正在维护。
HTTPS
如果访问的站点是HTTPS,那么浏览器在建立TCP连接之后,需要进行SSL/TLS握手过程,以确保通信过程是安全的。SSL/TLS握手过程包括以下步骤:
客户端发送
Client Hello报文:客户端向服务器发送Client Hello报文,报文中包含协议版本号、加密算法列表、随机数等:服务器收到Client Hello报文后,向客户端发送Server Hello报文,报文中包含协议版本号、加密算法、证书等信息。服务端发送证书:服务器向客户端发送一个数字证书,证书中包的公钥,以及证书颁发机构的签名等信息。客户端会验证证书的到客户端的
Client Key Exchange报文后,使用. 客户端发送Change Cipher Spec报文:客户端向服务器发送Change Cipher Spec报文,表示接下来的通信都将使用协商好的加密算法进行。客户端发送
Finished报文:客户端向服务器发送Finished报文,报文中包含一段哈希值,用于验证握手过程的完整性和准确性。服务端发送
Change Cipher Spec报文:服务器向客户端发送Change Cipher Spec报文。服务端发送
Finished报文:服务器向客户端发送Finished报文,用于验证握手过程的完整性和准确和服务器之间已经的影响。
CSS
两种盒模型
content-box:标准盒模型,width只包含contentborder-box:怪异盒模型,width包含content、border和padding
有的时候我们在使用padding时候会导致容器被左右撑开,这时候修改下box-sizing就行了。
选择器的权重
内联 > ID 选择器 > 类选择器 > 标签选择器
到具体的计算层⾯,优先级是由 A 、B、C、D 的值来决定的,其中它们的值计算规则如下:
如果存在内联样式,那么 A = 1, 否则 A = 0
B 的值等于 ID 选择器出现的次数
C 的值等于 类选择器 和 属性选择器 和 伪类 出现的总次数
D 的值等于 标签选择器 和 伪元素 出现的总次数
知道了优先级是如何计算之后,就来看看比较规则:
从左往右依次进行比较 ,较大者优先级更高
如果相等,则继续往右移动一位进行比较
如果 4 位全部相等,则后面的会覆盖前面的
经过上面的优先级计算规则,我们知道内联样式的优先级最高,如果外部样式需要覆盖内联样式,就需要使用!important
'+' 与 '~' 选择器有什么不同
+选择器匹配紧邻的兄弟元素~选择器匹配随后的所有兄弟元素
代码示例:
z-index 与层叠上下文
MDN 文档地址:层叠上下文 - CSS:层叠样式表 | MDN层叠上下文 - CSS:层叠样式表 | MDN
理解清楚下面这张图感觉应该也够了:

层叠上下文的计算往往是根据父元素来的,即在同一个父元素中进行z-index的比较,值较大者在上层。这种比较不会跨层级进行。
元素隐藏的几种方式以及区别
当我们要隐藏一个元素时,可以采用如下方式:
display: none:不占空间,完全消失,触发重绘和重排opacity: 0:本质就是变透明,依旧占空间visibility: hidden:占空间,但是在页面上不可见,仅触发重绘顺带一提,当设置元素 visibility: collapse 后,一般的元素的表现与 visibility: hidden 一样,也即其会占用空间。但如果该元素是与 table 相关的元素,其表现却跟 display: none 一样,也即其占用的空间会释放。
flex 布局
flex 布局的常用属性
父容器:
display: flexjustify-content、justify-items、align-items、align-content排列方向:
flex-direction换行:
flex-wrap
子容器:
flex-shrink、flex-basis、flex-grow、flex排列顺序:
orderalign-self
flex-shrink、flex-basis 和 flex-grow
flex的子容器设置flex-grow属性,可以实现子元素按照比例撑满容器的效果,如:
flex-basis用于设置元素的基础宽度;flex-shrink用于子元素的收缩。
align-self 和 align-items 的区别
align-items用于flex父容器,align-self用于flex子元素。align-self默认值为auto,表示继承父级flex容器的align-items。如果子元素设置了align-self,将以设置的align-self为主,不受align-items影响。
align-items 和 align-content 的区别
align-items用于定义flex子项在flex容器的当前行的侧轴(纵轴)方向上的对齐方式,即作用的是flex子项。
align-content和align-items作用相似,但作用的对象不同,align-content作用的是行(所以如果设置父容器的flex-wrap为no-wrap的时候align-content是不生效的)
提示
关于justify的各个属性的区别也大体与上面的相似,就不写了。
水平垂直居中方案
行内元素:
flex布局+justify-content:center+align-items:centerline-height: 父元素高度+text-align:center
块元素:
flex布局+justify-content:center+align-items:center父元素
relative+子元素absolute:子元素宽高已知:
top:50%+left:50%+负margin调整子元素位置子元素宽高未知:
top:50%+left:50%+transform:translate(50%)
代码实现如下:
左侧固定、右侧自适应
- 利用浮动(左侧元素 float: left,外界容器通过 overflow: auto 设置 BFC):
flex实现:
淘宝 UED 的双飞翼布局
实现要点如下:
容器内三元素浮动
中间元素填满容器
左右元素利用负
margin调整到正确的位置
下面是具体实现(CodePen缩放开0.5x,不然会变形):
link 和@import 的区别
在CSS中,link和@import都是用于引入外部样式表的方法,它们的区别如下:
加载时间:
link在页面加载时同时加载外部样式表,而@import在页面加载后再加载外部样式表。因此,当网页加载过慢时,使用@import可能会导致页面出现闪烁现象。浏览器兼容性:
link标签是 XHTML 标准属性,可以同时加载CSS和RSS等其他文件类型,而@import只能加载CSS文件,并且在早期版本的 IE 浏览器中存在兼容性问题。CSS 权重:由于
link标签在页面加载时就已经应用于元素,因此它的 CSS 样式具有比@import更高的优先级。在相同选择器的情况下,link引入的样式表会覆盖@import引入的样式表。DOM操作:link标签可以通过JavaScript进行DOM操作,而@import引入的样式表无法进行DOM操作。页面权重:
link标签会增加页面权重,而@import引入的样式表不会增加页面权重。
渐进增强和优雅降级
渐进增强和优雅降级是两种Web开发的设计理念,它们都是为了保证Web应用程序在不同的设备和浏览器上都能够正常运行,并且有良好的用户体验。
在设计Web应用程序时,渐进增强首先考虑的是基本的功能和用户体验,并且将这些功能和体验作为核心,在此基础上逐渐增加额外的功能和体验,其目标是确保所有用户都可以访问和使用基本的功能和体验,并且为那些拥有更先进浏览器或设备的用户提供额外的功能和体验。而优雅降级首先考虑最完整、最先进的功能和用户体验,并且将这些功能和体验作为核心,在此基础上逐渐降低功能和体验的要求,以适应那些拥有较低级别浏览器或设备的用户,其目标是确保所有用户都可以访问和使用一定程度的功能和体验,而不会因为浏览器或设备的限制而无法访问。
典型的例子如:
.transition {
/*渐进增强写法*/
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-o-transition: all 0.5s;
transition: all 0.5s;
}
.transition {
/*优雅降级写法*/
transition: all 0.5s;
-o-transition: all 0.5s;
-moz-transition: all 0.5s;
-webkit-transition: all 0.5s;
}