关于 Dom 的那些事儿

210 阅读11分钟

前言

本文总结了一些常用的 DOM 操作,主要是为了方便平时开发过程中的 api 查阅,记性不好老是容易健忘,刚好借此机会总结一波 🤣

什么是DOM

每个载入浏览器的 HTML 文档都会成为 Document 对象。

DOM(文档对象模型)是一种表示和操作HTML和XML文档的标准编程接口。它将文档解析为由节点和对象(包括元素,属性和文本)组成的树形结构,允许开发人员使用编程语言(如JavaScript)来操作文档的内容,结构和样式。

通过DOM,我们可以使用 JavaScript 来访问和修改文档的内容和结构,从而实现动态网页的创建和交互,对用户的操作做出响应。

什么是节点、元素

DOM 将 HTML 文档视作树结构,这种结构被称为节点树。

通过 HTML DOM,节点树中的所有节点都可以通过 JS 进行访问。所有 HTML 元素(节点)均可被修改。

经常使用的节点主要有以下几种类型:

  1. Element类型(元素节点):nodeType值为 1

    • HTML元素内的文本是文本节点
    • 每个HTML属性是属性节点
  2. Text类型(文本节点):元素内的文本是文本节点 nodeType值为 3

  3. Comment类型(注释节点): 每个注释是注释节点 nodeType值为 8

  4. Document类型(document节点):整个文档是一个文档节点 nodeType值为 9

其规定的一些常用的属性:

  • 通过 document.body document.head 可以分别获取HTML中的
  • document.documentElement为标签

通过一些属性可以来遍历节点树:

  1. parentNode -- 获取所选节点的父节点,最顶层的节点为#document

  2. childNodes -- 获取所选节点的子节点集合

  3. firstChild -- 获取所选节点的第一个子节点

  4. lastChild -- 获取所选节点的最后一个子节点

  5. nextSibling -- 获取所选节点的后一个兄弟节点 列表中最后一个节点的nextSibling属性值为null

  6. previousSibling -- 获取所选节点的前一兄弟节点

    • 列表中第一个节点的 previousSibling 属性值为 null

元素

元素(element)是指 HTML 或 XML 中的标签(tag),它们是文档树中的一种节点类型,也是 DOM 中最常用的节点类型之一。这就意味着元素继承自节点,所以它也具备了节点所拥有的属性、方法。

元素所具有属性:

    const element = document.getElementById('idName') 
    element.childNodes
    element.firstChild
    element.lastChild
    element.previousSibling
    element.nextSibling
    element.parentNode
    element.parentElement

获取 DOM

document 对象是一个内置对象,我们可以通过它的许多属性和方法来获取和操作 DOM 。

获取 DOM 的方式:

  1. 通过 id 获取

    • **document.getElementById('idName')
    • 由于在页面中标签元素的 id 是唯一的,所以返回的是一个 node 节点
  2. 通过 name 属性获取

    • document.getElementsByName('domName') 返回一个nodeList集合(类数组)
    • name 属性是为了方便提交表单数据而打造的,所以 name 属性一般存在于(form表单、表单元素、iframe、img)。
    • 设置 name 属性时,会自动在 Document 对象中创建以该 name 属性值命名的属性。所以可以通过document.domName 引用相应的 DOM 对象
  3. 通过指定的标签名

    • document.getElementsByTagName('li') 返回一个nodeList集合(类数组)
    • tagName不区分大小写,当tagName为*时,表示选取所有元素
    • 该方法只能选取调用该方法的元素的后代元素
  4. 通过class类名获取

    • document.getElementsByClassName(“classNames”) 返回一个nodeList集合(类数组)
    • classNames是CSS类名称的组合(多个类名之间用空格隔开且样式名称不分先后顺序)
    • getElementsByClassName(“class2 class1”) 将选取同时应用了class1和class2样式的元素
    • 该方法只能选取调用该方法的元素的后代元素
  5. 通过样式选择器获取元素

    • document.querySelectorAll('selector') , 其中selector为合法的CSS选择器,返回一个nodeList集合,没有找到则返回null

    • document.querySelector() 样式选择器,返回符合规则的第一个元素;如果没找到则返回null

  6. 伪元素的获取

    • 与DOM元素不同的是,伪元素的本身并不是DOM元素。它并不存在于文档中,所以无法直接通过 js 获取和操作,默认 display 属性为 inline
    • 常用的伪元素 除了::after、::before、之外还有 ::first-line、::first-letter、::selection、::backdrop
    • 需要通过 window.getComputedStyle(element, ":after") 来获取 DOM 上的伪元素

操作DOM

在页面中操作 DOM 是非常“昂贵”的,所以应该尽量避免频繁的对 DOM 进行操作。

  1. 新增/插入节点

    • 新增创建节点:document.createElement()
    • 创建一个文本节点: document.createTextNode('text')
    • 插入节点: element.appendChild(被插入的节点)

    <div id="wrapper">
    </div>
    //  获取节点
    const wrapper = document.getElementById('wrapper')
    //  创建文本节点
    const text = document.createTextNode('我是文本')

    //  新增节点
    const newDiv = document.createElement('div')
    newDiv.innerHTML = '我是内容'


    // 插入节点
    wrapper.appendChild(newDiv)
    wrapper.appendChild(text)

被插入的元素

  1. 获取子元素列表,获取父元素

    • 通过 parentNode 属性获取当前节点的父元素
    • 通过 childNodes 属性获取元素的子结点 nodeList 集合(类数组)

    newDiv.parentNode    // 获取所选节点的父节点

    wrapper.childNodes  // 获取元素的子结点

  1. 删除子元素

    • 删除节点: element.removeChild(节点) 并返回被移除的节点
    const removeChild = wrapper.removeChild(newDiv);//移除document对象的方法div1  
    console.log(removeChild)

  1. 元素替换

    • 替换节点: replaceChild(插入的节点,被替换的节点) ,用于替换节点
    • 接受两个参数,第一参数是要插入的节点,第二个是要被替换的节点。返回的是被替换的节点。

    const replaceChild = wrapper.replaceChild(text,newDiv);// 替换节点将 newDiv 替换成文本节点
    console.log(replaceChild)

属性操作

attribute 是HTML标签中定义的属性(修改html属性,会改变html结构),它的值只能是字符串。

  • element.attributes: 返回元素所有属性节点的实时集合,是个类数组对象

  • element.getAttributeNames(): 返回一个Array 包含指定元素的所有属性名称
  • element.getAttribute(attributeName): 获取元素的属性值
  • element.setAttribute(attributeName, value): 设置元素的属性值
  • element.removeAttribute('attributeName'): 删除元素对应的属性
  • element.hasAttribute(attributeName): 检查元素是否具有指定的属性
  • element.hasAttributes(): 检查元素是否具有任何属性。如果元素具有任何属性,则返回 true,否则为 false

所有HTML 元素都有一组默认属性,例如 id、class、style 等。这些属性可以通过上述方法进行操作。

可以通过元素的 src 属性设置图片的地址

    const img = document.querySelector('img');
    // 设置图片的 src
    img.setAttribute('src', 'image.jpg');
    //  获取src属性值
    const href = img.getAttribute('href');

类的操作

DOM 中同样可以对元素的类名进行操作设置

  • element.className -- 获取元素的类名,以字符串的形式返回
  • element.classList -- 获取元素的类名列表

className 是一个可读写的属性,可以直接在原有的类名上拼接新的名称,或者通过正则来删除、修改类名

    element.className += 'example'

    element.className = element.className.replace(/^bold$/, '');

classList 提供了操作类名的方法:

    element.classList.add()	// 新增一个类名
    element.classList.remove()	//移除一个指定的类名
    element.classList.contains()	// 检查当前元素是否包含某个类名
    element.classList.toggle()	//将某个类名 移入 或 移出 。
    element.classList.item()	//返回指定索引位置的类名。
    element.classList.toString()	//将所有类名拼接成字符串。

注意点区分

1. HTML中的 attribute 和 JavaScript中的 property

  • attribute 是 HTML 元素在 HTML 中的属性,可以通过 getAttribute 和 setAttribute 方法获取和设置。
  • property 是 HTML 元素在 DOM 中的属性,可以看作 DOM 对象的键值对,用点操作符修改,通过 JavaScript 直接访问和修改。

用点操作符修改 property的 value 值,并不会同步到 attribute 上;

但是通过 setAttribute 修改属性值,会同步到 property 上。

在HTML中 我们可以通过以下方式获取和设置 value 属性:

    <input type="text" id="myInput" value="Hello World">

    <script>
    	// 通过 attribute 获取 value 属性
      const myInput = document.querySelector('#myInput');
      const valueAttribute = myInput.getAttribute('value'); // 'Hello World'
      
      // 通过 property 获取 value 属性
      const valueProperty = myInput.value; // 'Hello World'
      
      // 通过 property 设置 value 属性
      myInput.value = 'New Value';

    </script>

虽然 attribute 和 property 定义的属性分别在不同层面上,但是有些属性值是共享的例如:
id、class、lang、dir、title

    <div id="div" class="class" lang="lang" dir="dir" title="title" user="user" >


     var ele = document.getElementById('id');
     // 获取 元素 HTML 上的 属性
     console.log(ele.getAttribute('id'))    // id
     console.log(ele.getAttribute('class')) // class
     console.log(ele.getAttribute('lang'))  // lang
     console.log(ele.getAttribute('dir'))   // ltr
     console.log(ele.getAttribute('title')) // title
     console.log(ele.getAttribute('user'))  // user

    // 点操作符获取 DOM 上的属性
     ele.user1= 'user1'
     console.log(ele.id)                    // id
     console.log(ele.className)             // class
     console.log(ele.lang)                  // lang
     console.log(ele.dir)                   // ltr
     console.log(ele.title)                 // title
     console.log(ele.user1)                 // user

     console.log(ele.user)                  // undefined
    //  点操作符不会同步到 元素 HTML 的 attribute 上
     console.log(ele.getAttribute('user1')) // null

但 attribute 和 property 的值并不总是相同的:

  1. 表单中的单选项,对于 input 元素的 checked 属性,

    • 在 HTML 中可以设置为 checked 或者 unchecked;
    • 在 JavaScript 中,其值只有 true 和 false 两种情况。
    <input type='radio' checked='checked' id='radio'>
    <script>
      var radio = document.getElementById('radio');
      console.log(radio.getAttribute('checked')); // 'check'
      console.log(radio.checked); // true
    </script>
  1. img 中的 href 属性,通过不同方式获取到的路径也有所不同
    <a href='a.html' id='web'> </a>
    <script>
      var radio = document.getElementById('web');
      console.log(web.getAttribute('href')); // 'a.html' 
      console.log(web.href); // 绝对路径
    </script>
  • attribute 取到的是相对路径;
  • property 取到的是绝对路径。

attribute 和 property 的使用

再次强调 DOM 的操作非常“昂贵”,所以尽量避免频繁的DOM操作。

使用 property 可以在 js 的机制中避免 DOM 的重新渲染,而 attribute 一旦改了 html 结构,则一定会引起DOM 的重新渲染。

所以尽量使用property。

因此 获取 DOM 元素最新的属性值,应该使用 property,获取 HTML 中的属性值,则应该使用 attribute

建议在以下两种情况中使用attribute:

  1. 自定义 attribute 标签,因为它不能同步到 property 上。
  2. 访问内置的 html 标签的 attribute,如 input 的 value(可以用来检验 value 值是否改变)

2. innerText、innerHTML、outText、outHTML

注意区分元素的这几个属性

估计大家在刷面试题的时候也看到过这样的题目:

描述一下 innerText、 innerHTML、outText、outHTML 这几个属性之间的区别

比如:div id="testDiv"><p>Text in DIV</p></div>

先来写一个小小的demo来观察一下

    <!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>
      <div id="testDiv"><p>Text in DIV</p></div>

      <script>
        const testDiv = document.getElementById('testDiv')
        console.log('innerHTML -- ', testDiv.innerHTML)
        console.log('innerText -- ', testDiv.innerText)
        console.log('outerHTML -- ', testDiv.outerHTML)
        console.log('outerText -- ', testDiv.outerText)
      </script>
      
      
    </body>
    </html>

通过控制台的输出我们可以观察到:

  • innerHTML: 用于获取或设置元素的 HTML 内容,可以获取元素的所有子元素、文本和 HTML 标记
  • innerText:用于获取或设置元素的文本内容,但不包括 HTML 标记。
  • outerHTML:用于获取或设置元素及其所有子元素的 HTML 内容,包括其自身 HTML 标记
  • outerText:属性用于获取或设置元素及其所有子元素的文本内容,但不包括 HTML 标记

这些属性支持读取和赋值,但 outerText、nnerText 的区别在于 outerText 赋值时会把标签一起赋值掉。

另外 outerText、innerText 赋值出现HTML标签或者特殊字符时会被转义。

需要注意的是,这些属性在访问或设置时都会重新解析和渲染 HTML 内容,因此在性能方面可能会有一定的影响。

从页面中获取的宽高信息

获取DOM 元素高度:

  • offsetHeight:返回元素的高度,包括元素的边框、内边距和滚动条,但不包括外边距。

    • 网页可见区域高: document.body.offsetHeight (包括边线的高)
  • clientHeight: 返回元素的高度,包括元素的内边距,但不包括边框、外边距和滚动条。

    • 网页可见区域高: document.body.clientHeight
  • scrollHeight: 返回元素的内容高度,包括被隐藏的部分,但不包括边框、外边距和滚动条。

    • 网页正文全文高: document.body.scrollHeight
  • scr ollTop:用于获取或设置滚动条顶部与元素顶部的距离。它适用于所有具有滚动条的元素,例如 window、document 和 div 等。

    • 网页被卷去的高: document.body.scrollTop
    • document.scrollTop = document.scrollHeight - document.clientHeight
  • getBoundingClientRect().height:返回元素的高度,包括元素的边框,但不包括内边距、外边距和滚动条。
  • style.height:返回或设置元素的高度,包括内边距和边框,但不包括外边距和滚动条。

DOM 元素的宽度与高度的获取相似

  • offsetWidth:返回元素的宽度,包括元素的边框、内边距和滚动条,但不包括外边距。
    • 网页可见区域宽: document.body.offsetWidth (包括边线的宽)
  • clientWidth:返回元素的宽度,包括元素的内边距,但不包括边框、外边距和滚动条。

    • 网页可见区域宽: document.body.clientWidth
  • scrollWidth:返回元素的内容宽度,包括被隐藏的部分,但不包括边框、外边距和滚动条。

    • 网页正文全文宽: document.body.scrollWidth
  • scrollLeft:用于获取或设置滚动条左侧与元素左侧的距离。它适用于所有具有水平滚动条的元素,例如 window、document 和 div 等。

    • 网页被卷去的左: document.body.scrollLeft
  • getBoundingClientRect().width:返回元素的宽度,包括元素的边框,但不包括内边距、外边距和滚动条。
  • style.width:返回或设置元素的宽度,包括内边距和边框,但不包括外边距和滚动条。

这些属性需要注意的是:

  1. style.width 只能获取或设置内联样式中的宽度值。如果元素使用了外部样式表或者内部样式表,需要使用其他方法来获取宽度。
  2. getBoundingClientRect() 返回的是一个 DOMRect 对象,包括元素的位置和尺寸信息,可以通过 width 属性获取元素的宽度。

    const element = document.getElementById('myElement');

    // 获取元素实际的宽度
    const offsetWidth = element.offsetWidth;
    // 获取元素实际的高度
    const offsetHeight = element.offsetHeight;
    // 元素的实际距离左边界的距离
    const offsetLeft = element.offsetLeft;
    // 元素的实际距离上边界的距离
    const offsetTop = element.offsetTop;

    const clientHeight = element.clientHeight;
    const clientWidth = element.clientWidth;

    const scrollHeight = element.scrollHeight;
    const scrollWidth = element.scrollWidth;

总结

DOM是Web开发中不可或缺的一部分,它提供了一种方便的方式来访问和操作HTML和XML文档。

理解 DOM 的结构和 API ,并且注意DOM的优化,夯实学习基础,有助于开发高效、响应式和可维护的Web应用程序。

特别是对 DOM 元素的宽高获取总是常常忘记😂,如果你也有这方面的困扰就赶紧收藏起来吧!!!

如果还有需要补充的知识点或者是常用 api 可以在评论区滴滴我啊,我会持续更新的 🎈!

参考

  1. DOM常用操作
  2. 通过JS如何获取DOM节点元素