CSS必须了解的底层基础

137 阅读14分钟

浏览器的渲染过程

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:表示整个文档的节点。
  • 常用属性和方法

    • nodeName:获取节点的名称。
    • nodeValue:获取或设置节点的值。
    • parentNode:获取节点的父节点。
    • childNodes:获取节点的所有子节点。
    • appendChild():将一个节点添加到该节点下。

4. Text 对象

Text 对象代表文本节点,它是文档中的文本内容。文本节点本身不包含标签或属性,只包含纯文本。

  • 常用属性

    • textContent:获取或设置文本节点的内容。
    • nodeValue:获取或设置文本节点的值。

5. Attribute 对象

Attribute 对象代表元素的一个属性,HTML 标签中的属性(如 idclasshref 等)在 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:事件的类型(如 clickkeydown)。
    • target:事件发生的目标元素。
    • currentTarget:当前处理该事件的元素。
    • preventDefault():阻止事件的默认行为。
    • stopPropagation():停止事件的传播。

9. HTMLCollection 和 NodeList 对象

  • HTMLCollection:是一个类似数组的对象,表示文档中所有与特定选择器匹配的元素集合(如 getElementsByTagName()getElementsByClassName() 方法返回的对象)。
  • NodeList:表示一个节点列表,可以是元素节点的集合(如 querySelectorAll() 返回的对象)或其他类型的节点集合。与 HTMLCollection 相似,但 NodeList 有时是“实时”更新的(动态更新)。

渲染过程

  • 当浏览器加载 HTML 页面时,它会将 HTML 标签(如 <div><p> 等)转化为 DOM 元素节点。这些节点表示了页面中的元素,比如 <div><span><h1> 等。每个元素节点都包含了标签名、属性(如 idclass 等)以及子元素等信息。
  • CSS 不会直接转换为 DOM 节点,而是会影响这些 DOM 元素节点的 样式。浏览器- 解析 CSS 文件(或内嵌的 <style> 标签)并将其应用到对应的 DOM 元素节点上,构成 CSSOM 树

DOM 树和 CSSOM 树结合的过程是:

  1. 解析 HTML 文件:浏览器将 HTML 文档解析成 DOM 树。
  2. 解析 CSS 文件:浏览器将 CSS 文件(或内联样式)解析成 CSSOM 树。
  3. 合并 DOM 树和 CSSOM 树:浏览器将 DOM 树和 CSSOM 树合并在一起,生成 渲染树(Render Tree)。
  4. 渲染树绘制:渲染树包含了所有可见元素的信息,并且它会被用来计算元素的位置和绘制到屏幕上。

这里举个小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> 元素,并将其背景色设置为浅灰色。属性选择器非常适合用于根据元素的属性来选中元素。

选择器优化

  1. 避免过度依赖后代选择器 (A B):

    • 使用后代选择器可能会导致浏览器对整个 DOM 树进行遍历,性能较差。尽量减少使用深层次的后代选择器,改用子元素选择器 (A > B) 或通过更精确的类名选择器来提高效率。
  2. 优先使用类选择器 (.class):

    • 类选择器通常是浏览器渲染性能最佳的选择。相比于通用选择器(如 div 或 *),类选择器通过缩小匹配范围,能显著提升性能。
  3. 避免过多的嵌套选择器:

    • 过深的选择器链(如 div ul li a)会增加计算的复杂度。适当简化选择器链,可以减少浏览器的计算量。
  4. 利用ID选择器优化 (#id):

    • ID选择器是非常高效的,因为它是唯一的并且会建立索引。若能通过ID定位元素,尽量使用ID选择器而非类选择器或其他更复杂的选择器。
  5. 避免不必要的通用兄弟选择器 (A ~ B):

    • 通用兄弟选择器会扫描整个父级节点,匹配所有符合条件的兄弟元素,性能较差。应尽量避免或优化这种选择器。
  6. 合并相同的选择器:

    • 通过合并相同样式的选择器来减少重复的规则,提升代码可维护性和性能。例如:

      .btn-primary, .btn-secondary {
        padding: 10px;
      }
      
  7. 减少使用属性选择器:

    • 属性选择器(如 input[type="text"])在某些情况下可能效率较低,尤其是对于大量元素时。如果可能,考虑使用类选择器替代属性选择器。
  8. 避免过多的复杂选择器组合:

    • 复杂的选择器组合(如 div > .class1 + .class2)可能导致浏览器无法快速匹配目标元素。适当简化组合,特别是避免频繁使用 > 和 + 组合。