浏览器的渲染过程
DOM
DOM是浏览器提供的它是浏览器提供的一种接口,用于通过编程方式访问和操作网页的内容和结构。DOM 将网页的每个元素(如标签、文本、属性等)表示为一个对象,这些对象是可以被编程操作的,包括创建、删除、修改节点,处理事件,修改样式等等。
浏览器会将网页的内容解析成DOM树的形式,这样能够更高效的操作和管理网页的内容。
简单来说,就是为了高效的处理HTML页面的数据,浏览器会将HTML标签变成一个个对象。这些对象会存储在一个树形结构中。我们想要修改对象的内容,可以使用js等语言通过浏览器提供的接口(DOM)来进行修改。
DOM常见对象:
1. Document 对象
Document 对象是整个文档的入口点,代表了整个 HTML 或 XML 文档。它是 DOM 中的根对象,所有其他对象都可以通过它进行访问。
-
常用属性和方法:
document.getElementById():通过 ID 获取元素。document.getElementsByTagName():通过标签名获取元素集合。document.querySelector():通过 CSS 选择器获取第一个匹配的元素。document.createElement():创建一个新的元素节点。document.createTextNode():创建一个新的文本节点。
2. Element 对象
Element 对象代表文档中的一个元素节点(如 <div>、<p>、<span> 等 HTML 标签)。它是 DOM 树中的基本构建块,包含了标签的属性、子元素和内容。
-
常用属性和方法:
element.id:获取或设置元素的 ID。element.className:获取或设置元素的类名。element.innerHTML:获取或设置元素的 HTML 内容。element.setAttribute():设置元素的属性。element.getAttribute():获取元素的属性值。element.appendChild():将子节点添加到该元素中。
3. Node 对象
Node 是 DOM 中的基本接口,所有文档中的节点都继承自 Node。它有多个子类,表示不同类型的节点(如元素节点、文本节点、属性节点等)。
-
常见的子类:
- Element:元素节点(如
<div>、<span>)。 - Text:文本节点,表示节点内的文本内容。
- Comment:注释节点,表示 HTML 或 XML 中的注释。
- Document:表示整个文档的节点。
- Element:元素节点(如
-
常用属性和方法:
nodeName:获取节点的名称。nodeValue:获取或设置节点的值。parentNode:获取节点的父节点。childNodes:获取节点的所有子节点。appendChild():将一个节点添加到该节点下。
4. Text 对象
Text 对象代表文本节点,它是文档中的文本内容。文本节点本身不包含标签或属性,只包含纯文本。
-
常用属性:
textContent:获取或设置文本节点的内容。nodeValue:获取或设置文本节点的值。
5. Attribute 对象
Attribute 对象代表元素的一个属性,HTML 标签中的属性(如 id、class、href 等)在 DOM 中作为 Attribute 对象存在。
-
常用属性和方法:
name:获取属性的名称。value:获取或设置属性的值。specified:检查属性是否已设置。
6. DocumentFragment 对象
DocumentFragment 是一个轻量级的文档对象,通常用于将一组节点作为一个整体进行操作。它没有实际的表现,仅在内存中存储节点,适用于批量操作,避免频繁的重排和重绘。
-
常用方法:
appendChild():将节点添加到文档片段。cloneNode():克隆片段中的内容。
7. Window 对象
Window 对象表示浏览器窗口,DOM 与浏览器环境密切相关,window 对象提供了访问浏览器窗口的各种方法和属性。
-
常用属性和方法:
window.document:获取当前窗口的文档对象。window.alert():显示一个警告框。window.setTimeout():设置定时器。window.setInterval():设置周期性定时器。
8. Event 对象
Event 对象代表浏览器中的各种事件(如鼠标点击、键盘输入、表单提交等)。它包含了事件的相关信息,如事件类型、目标元素、鼠标位置等。
-
常用属性:
type:事件的类型(如click、keydown)。target:事件发生的目标元素。currentTarget:当前处理该事件的元素。preventDefault():阻止事件的默认行为。stopPropagation():停止事件的传播。
9. HTMLCollection 和 NodeList 对象
- HTMLCollection:是一个类似数组的对象,表示文档中所有与特定选择器匹配的元素集合(如
getElementsByTagName()和getElementsByClassName()方法返回的对象)。 - NodeList:表示一个节点列表,可以是元素节点的集合(如
querySelectorAll()返回的对象)或其他类型的节点集合。与HTMLCollection相似,但NodeList有时是“实时”更新的(动态更新)。
渲染过程
- 当浏览器加载 HTML 页面时,它会将 HTML 标签(如
<div>、<p>等)转化为 DOM 元素节点。这些节点表示了页面中的元素,比如<div>、<span>、<h1>等。每个元素节点都包含了标签名、属性(如id、class等)以及子元素等信息。 - CSS 不会直接转换为 DOM 节点,而是会影响这些 DOM 元素节点的 样式。浏览器- 解析 CSS 文件(或内嵌的
<style>标签)并将其应用到对应的 DOM 元素节点上,构成 CSSOM 树。
DOM 树和 CSSOM 树结合的过程是:
- 解析 HTML 文件:浏览器将 HTML 文档解析成 DOM 树。
- 解析 CSS 文件:浏览器将 CSS 文件(或内联样式)解析成 CSSOM 树。
- 合并 DOM 树和 CSSOM 树:浏览器将 DOM 树和 CSSOM 树合并在一起,生成 渲染树(Render Tree)。
- 渲染树绘制:渲染树包含了所有可见元素的信息,并且它会被用来计算元素的位置和绘制到屏幕上。
这里举个小demo来帮助同学们理解。
DOM 树:
浏览器将 HTML 内容解析为 DOM 树,结构如下:
- Document
- HTML
- HEAD
- META (charset="UTF-8")
- TITLE (CSSOM 和 DOM 示例)
- LINK (rel="stylesheet", href="styles.css")
- BODY
- DIV (id="container")
- P (class="text")
- TEXT (Hello, world!)
CSSOM 树:
浏览器将 CSS 内容解析成 CSSOM 树,表示样式信息:
- CSSOM
- Rule (selector: #container)
- background-color: lightblue
- padding: 20px
- Rule (selector: .text)
- color: red
- font-size: 20px
合并 DOM 树和 CSSOM 树:
浏览器将 DOM 树和 CSSOM 树结合,构成渲染树(Render Tree)。这个渲染树不仅包括 DOM 树的结构,还包括每个元素的样式信息。
对于这个例子,合并后的渲染树可能如下:
- Render Tree
- DIV (id="container")
- background-color: lightblue
- padding: 20px
- P (class="text")
- color: red
- font-size: 20px
- TEXT (Hello, world!)
浏览器会逐行读取HTML文件,并解析出对应的DOM节点,构建DOM树。浏览器在解析HTML的过程中会逐步构建DOM结构。这个过程是一个 增量构建的过程,即边解析边构建DOM。所以我们通常将css 样式在开头引入(防止页面渲染出没有样式的HTML后闪烁),将js修改DOM的文件放在body下面(防止找不到Node节点)
CSS的优先级
大家有没有想过为什么称之为层叠样式表?
因为 选择符会从上到下按照层叠关系匹配。说具体一点,假设多个选择符都给一个元素 应用了样式,那么分数高的规则就会覆盖分数低的规则。也就是说
- 如果是同一属性:例如多个规则都试图设置
color,最终的值会由优先级最高的规则决定。如果优先级相同,则后出现的规则会覆盖之前的规则。 - render tree 中的最终属性:只有最终确定的属性会被应用到渲染树中的元素。如果多个规则适用并且优先级不同,最终的属性值会是 优先级最高的那个规则 或 源顺序最靠后的那个规则。
1. 优先级的计算规则
CSS的优先级从小到大进行比较,计算的基本规则如下:
- 元素选择器的优先级最小,为1。
- 类选择器的优先级略高,为10。
- ID选择器的优先级更高,为100。
- 行内样式的优先级最高,为1000。
!important声明的样式优先级最高,优先级值为无上限,它覆盖其他任何规则。
来举个例子便于大家理解哈。下面这个实例中的分数分别用注释给写出了,他们都作用于同一元素p,选择器会选择最高的分数的样式给p赋予属性。
<style>
/*1*/
p{
color: blue;
}
/*11*/
.container p{
color: red;
}
/*101*/
#main p{
color: green;
}
</style>
</head>
<body>
<!-- 属性 -->
<div class="container" id="main">
<p style="color: pink;">这是一段威哥的爱情故事</p>
</div>
</body>
CSS的选择器
一、基础选择器
1. 标签选择器(Element Selector)
p {
color: blue;
}
标签选择器的优点是简洁直观,但它的作用范围较广,可能会影响到页面上所有的该类型元素。
2. 类选择器(Class Selector)
类选择器使用元素的 class 属性来选中具有特定类的元素。它通常以一个点(.)符号开头。例如:
.button {
background-color: red;
}
类选择器可以被多个元素共享,因此它具有更高的灵活性和复用性。
3. ID 选择器(ID Selector)
ID 选择器通过元素的 id 属性来选中页面中唯一的某个元素。它以井号(#)符号开头。例如:
#header {
font-size: 24px;
}
4. 通配符选择器(Universal Selector)
通配符选择器用 * 符号表示,它会选中页面中的所有元素。例如:
* {
margin: 0;
padding: 0;
}
这段代码会将页面中所有元素的外边距和内边距设置为 0。虽然通配符选择器使用简单,但它会影响页面上所有元素,因此在性能上可能会产生较大影响,尤其是当页面元素较多时。
二、组合选择器
1. 后代选择器(Descendant Selector)- 空格
后代选择器用空格( )表示,它选中的是某个元素内部的所有后代元素。例如:
div p {
color: green;
}
该代码会选中所有 <div> 标签内的 <p> 标签,并将它们的文字颜色设置为绿色。后代选择器是最常见的一种组合选择器,可以帮助我们选中父元素内部的特定子元素。
2. 子选择器(Child Selector)- >
子选择器用大于号(>)表示,它选中的是某个元素的直接子元素。例如:
ul > li {
list-style-type: none;
}
这段代码会选中所有 <ul> 标签下的直接 <li> 子元素,并将它们的列表样式设置为无。这与后代选择器不同,后代选择器会选中所有的后代元素,而子选择器只选中直接子元素。
3. 相邻兄弟选择器(Adjacent Sibling Selector)- +
相邻兄弟选择器用加号(+)表示,它选中的是紧接在某个元素之后的兄弟元素。例如:
h2 + p {
margin-top: 10px;
}
这段代码会选中紧接在 <h2> 标签后的第一个 <p> 标签,并给它设置上外边距。相邻兄弟选择器只会选中紧邻的兄弟元素,而不会选中所有的同级元素。
4. 通用兄弟选择器(General Sibling Selector)- ~
通用兄弟选择器用波浪线(~)表示,它选中的是某个元素后面的所有兄弟元素。例如:
h2 ~ p {
color: purple;
}
这段代码会选中所有紧接在 <h2> 标签后面的 <p> 标签,并将其文字颜色设置为紫色。与相邻兄弟选择器不同,通用兄弟选择器选中的是所有在同一个父元素下,且在目标元素之后的兄弟元素。
三、伪类选择器
伪类选择器用于选择某些特定状态或位置的元素,常见的伪类选择器包括:
1. ::active
::active 伪类用于选中正在被激活的元素,例如用户点击的按钮:
a:active {
color: red;
}
2. ::hover
::hover 伪类用于选中鼠标悬停时的元素,例如鼠标悬停在链接上时:
a:hover {
text-decoration: underline;
}
3. ::selection
::selection 伪类用于选中被用户选中的文本:
::selection {
background-color: yellow;
}
这段代码会将被选中的文本背景色设置为黄色。
4. :nth-child()
作用:选中父元素中的某个特定子元素,基于其顺序位置。nth-child() 可以使用数字、公式(如 2n, odd, even 等)来选中某个特定的子元素。
语法:
li:nth-child(2) {
color: blue;
}
选中 <ul> 或 <ol> 中的第二个 <li> 元素,并将其文字颜色设为蓝色。
更多例子:
:nth-child(odd):选中所有奇数位置的子元素。:nth-child(even):选中所有偶数位置的子元素。:nth-child(2n+1):选中所有奇数位置的元素,从第一个开始
伪类组合
伪类选择器还可以组合使用,这样可以使样式更加灵活和具体。
例如:
a:hover, a:active {
color: red;
}
这段代码会使所有的链接,在鼠标悬停(:hover)和点击(:active)时,文字颜色变成红色。
input:checked + label{
color: blue;
}
这段代码会当input标签被选中时将相邻的label变成蓝色。
.container p:nth-child(3){
background-color:yellow ;
color: black;
}
.container p:nth-of-type(3){
background-color: lightblue;
color: black;
}
第一个会将找到的container容器下的第3个元素同时是p标签的元素选择。 第二个会将找到的container容器下的第三个p标签的元素选择。
如果你只是写 div:nth-child(1) 而没有指定父元素,浏览器会在 文档的上下文中 查找符合条件的元素,默认情况下,它会从根元素 <html> 的子元素开始查找,但这并不意味着 x 默认为 html,而是 div:nth-child(1) 会查找 html 元素下的所有直接子元素 中是否有一个 div 是第一个子元素。
四、伪元素选择器
伪元素选择器用于选中元素的特定部分,如元素的内容之前或之后的部分。常见的伪元素选择器包括:
::before:选中元素的开始位置之前的内容。::after:选中元素的结束位置之后的内容。
例如:
p::before{
content: "Prefix: ";
}
这段代码会在所有 <p> 标签的内容之前添加前缀文本 "Prefix: "。
五、属性选择器
属性选择器用于选择具有特定属性的元素。例如:
input[type="text"] {
background-color: lightgray;
}
该代码会选中所有 type="text" 的 <input> 元素,并将其背景色设置为浅灰色。属性选择器非常适合用于根据元素的属性来选中元素。
选择器优化
-
避免过度依赖后代选择器 (
A B):- 使用后代选择器可能会导致浏览器对整个 DOM 树进行遍历,性能较差。尽量减少使用深层次的后代选择器,改用子元素选择器 (
A > B) 或通过更精确的类名选择器来提高效率。
- 使用后代选择器可能会导致浏览器对整个 DOM 树进行遍历,性能较差。尽量减少使用深层次的后代选择器,改用子元素选择器 (
-
优先使用类选择器 (
.class):- 类选择器通常是浏览器渲染性能最佳的选择。相比于通用选择器(如
div或*),类选择器通过缩小匹配范围,能显著提升性能。
- 类选择器通常是浏览器渲染性能最佳的选择。相比于通用选择器(如
-
避免过多的嵌套选择器:
- 过深的选择器链(如
div ul li a)会增加计算的复杂度。适当简化选择器链,可以减少浏览器的计算量。
- 过深的选择器链(如
-
利用ID选择器优化 (
#id):- ID选择器是非常高效的,因为它是唯一的并且会建立索引。若能通过ID定位元素,尽量使用ID选择器而非类选择器或其他更复杂的选择器。
-
避免不必要的通用兄弟选择器 (
A ~ B):- 通用兄弟选择器会扫描整个父级节点,匹配所有符合条件的兄弟元素,性能较差。应尽量避免或优化这种选择器。
-
合并相同的选择器:
-
通过合并相同样式的选择器来减少重复的规则,提升代码可维护性和性能。例如:
.btn-primary, .btn-secondary { padding: 10px; }
-
-
减少使用属性选择器:
- 属性选择器(如
input[type="text"])在某些情况下可能效率较低,尤其是对于大量元素时。如果可能,考虑使用类选择器替代属性选择器。
- 属性选择器(如
-
避免过多的复杂选择器组合:
- 复杂的选择器组合(如
div > .class1 + .class2)可能导致浏览器无法快速匹配目标元素。适当简化组合,特别是避免频繁使用>和+组合。
- 复杂的选择器组合(如