JavaScript -- 10. 一文了解DOM对象及常用方法
创始人
2024-04-19 18:00:06
0

文章目录

  • 1. DOM介绍
    • 1.1 什么是DOM
    • 1.2 概念
    • 1.3 关系
    • 1.4 HelloWorld
    • 1.5 document对象
  • 2. DOM节点
    • 2.1 元素节点
      • 2.1.1 获取已有的元素节点
        • 2.1.1 `document.getElementById()`
        • 2.1.2 `document.getElementsByClassName()`
        • 2.1.3 `document.getElementsByTagName()`
        • 2.1.4 `document.getElementsByTagName()`
        • 2.1.5 `document.getElementsByName()`
        • 2.1.6 `document.querySelectorAll()`
        • 2.1.7 `document.querySelector()`
        • 2.1.8 `document.createElement()`
      • 2.1.2 通过某个元素获取其他节点
    • 2.2 文本节点
    • 2.3 属性节点(Attr)
    • 2.4 事件
      • 2.4.1 直接在元素属性中设置
      • 2.4.2 设置回调函数
      • 2.4.3 元素.addActionListener()
    • 2.5 文档的加载
    • 2.6 练习
      • 2.6.1 切换图片
      • 2.6.2 实现按钮之间相互配合
  • 3. DOM的修改
    • 3.1 添加节点
    • 3.2 删除节点
    • 3.3 练习
    • 3.4 复制节点
    • 3.5 修改CSS样式
    • 3.6 读取CSS样式
      • 3.6.1 getComputedStyle()
      • 3.6.2 通过属性读取样式
    • 3.7 修改class
  • 4. 事件
    • 4.1 事件对象简介
    • 4.2 Event对象
    • 4.3 冒泡
    • 4.4 事件的委派
    • 4.5 事件的捕获
  • 5. 其他
    • 5.1 定时器
      • 5.1.1 单次定时器
      • 5.1.2 多次定时器
      • 5.1.3 原理
    • 5.2 事件循环
    • 5.3 消息队列

1. DOM介绍

DOM – 李立超 | lilichao.com

前面在学习JS的时候发现,似乎JS和网页并没有太大的关系。换句话说,我们所编写的JS代码,除了是写在网页中以外,并没有和网页产生任何实质的联系。

而DOM就是一种使用JS来操作网页的技术

1.1 什么是DOM

DOM,全称Document Object Model,中文翻译为文档对象模型。

DOM属于Web API的一部分。Web API中定义了非常多的对象,通过这些对象可以完成对网页的各种操作(添加删除元素、发送请求、操作浏览器等)

  • DOM中的D意为Document,即文档。所谓文档就是指整个网页,换言之,DOM是用来操作网页的。
  • O意为Object,即对象。DOM将网页中的每一部分内容都转换为了对象。
  • M意为Model,即模型。模型用来表示对象之间的关系,也就是父子元素、祖先后代、兄弟元素等,明确关系后我们便可以通过任意一个对象去获取其他的对象。


My Title

A Heading

Link Text

对于上面的代码,我们可以得到如下DOM树

img

1.2 概念

在DOM标准下,网页中的每一个部分都会转换为对象。这些对象有一个共同的称呼——节点(Node)。

一个页面将会由多个节点构成,虽然都称为节点,但是它们却有着不同的类型:

  1. 文档节点
  2. 元素节点
  3. 文本节点
  4. 属性节点

每一个节点都有其不同的作用,文档节点表示整个网页,元素节点表示某个标签,文本节点表示网页中的文本内容,属性节点表示标签中的各种属性。如果从对象的结构上来讲,这些对象都有一个共同的父类Node。总的来说,都是属于节点,但是具体类型不同。

1.3 关系

  • 祖先 —— 包含后代元素的元素是祖先元素
  • 后代 —— 被祖先元素包含的元素是后代元素
  • 父 —— 直接包含子元素的元素是父元素
  • 子 —— 直接被父元素包含的元素是子元素
  • 兄弟 —— 拥有相同父元素的元素是兄弟元素

1.4 HelloWorld

要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象,才能去完成各种操作

所以浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用document代表的是整个的网页



Title






1.5 document对象

  • document对象表示的是整个网页
  • document对象的原型链:HTMLDocument -> Document -> Node -> EventTarget -> Object.prototype -> null
  • 凡是在原型链上存在的对象的属性和方法都可以通过Document去调用
  • 部分属性:
    • document.documentElement --> html根元素
    • document.head --> head元素
    • document.title --> title元素
    • document.body --> body元素
    • document.links --> 获取页面中所有的超链接

2. DOM节点

2.1 元素节点

  • 元素节点对象(element)
  • 在网页中,每一个标签都是一个元素节点
  • 如何获取元素节点对象?
    • 通过document对象来获取元素节点
    • 通过document对象来创建元素节点

2.1.1 获取已有的元素节点

const btn = document.getElementById("btn")const spans = document.getElementsByClassName("s1")const divs = document.getElementsByTagName("div")const genderInput = document.getElementsByName("gender")const divs2 = document.querySelectorAll("div")const div = document.querySelector("div")const h2 = document.createElement("h2")console.log(spans)
for(let i=0; ialert(spans[i])spans[i].innerText = "哈哈哈"+i
}

2.1.1 document.getElementById()

根据id获取一个元素节点对象

2.1.2 document.getElementsByClassName()

  • 根据元素的class属性值获取一组元素节点对象
  • 返回的是一个类数组对象
  • 该方法返回的结果是一个实时更新的集合
  • 当网页中新添加元素时,集合也会实时的刷新

2.1.3 document.getElementsByTagName()

  • 根据标签名获取一组元素节点对象
  • 返回的结果是可以实时更新的集合

2.1.4 document.getElementsByTagName()

获取页面中所有的元素

2.1.5 document.getElementsByName()

  • 根据name属性获取一组元素节点对象
  • 返回一个实时更新的集合
  • 主要用于表单项

2.1.6 document.querySelectorAll()

  • 根据选择器去页面中查询元素
  • 会返回一个类数组(不会实时更新)

2.1.7 document.querySelector()

根据选择器去页面中查询第一个符合条件的元素

2.1.8 document.createElement()

创建一个元素节点

根据标签名创建一个元素节点对象

2.1.2 通过某个元素获取其他节点

div元素的原型链

image-20221206183833372

image-20221206183512969

通过元素节点对象获取其他节点的方法

  • element.childNodes:获取当前元素的子节点(会包含空白的子节点
  • element.children:获取当前元素的子元素,不包含文本节点,使用更多一些
  • element.firstElementChild:获取当前元素的第一个子元素
  • element.lastElementChild:获取当前元素的最后一个子元素
  • element.nextElementSibling:获取当前元素的下一个兄弟元素
  • element.previousElementSibling:获取当前元素的前一个兄弟元素
  • element.parentNode:获取当前元素的父节点
  • element.tagName:获取当前元素的标签名

2.2 文本节点

在DOM中,网页中所有的文本内容都是文本节点对象,可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做

我们可以直接通过元素去修改其中的文本,修改文本的三个属性

  • element.textContent
    • 获取或修改元素中的文本内容
    • 获取的是标签中的内容,不会考虑css样式,所有的原始文本,包括换行和空格
  • element.innerText
    • 获取或修改元素中的文本内容
    • innerText获取内容时,会考虑css样式
      • 例如设置为display: none通过innerText就获取不到文本,通过textContent就可以获取到文本
    • 通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式)
    • 当字符串中有标签时,会自动对标签进行转义:
    • --> <li>,所以在网页上还是会原样显示出来添加的文本
  • element.innerHTML
    • 获取或修改元素中的html代码
    • 可以直接向元素中添加html代码
    • innerHTML插入内容时,有被xss注入的风险

2.3 属性节点(Attr)

  • 在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作
  • 如何操作属性节点:
    • 方式一:

      • 读取:元素.属性名(注意,class属性需要使用className来读取),读取一个布尔值时,会返回true或false
      • 修改:元素.属性名 = 属性值
    • 方式二:

      • 读取:元素.getAttribute(属性名)
      • 修改:元素.setAttribute(属性名, 属性值)
      • 删除:元素.removeAttribute(属性名)

Document

2.4 事件

事件(event)

  • 事件就是用户和页面之间发生的交互行为

    • 比如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键…
  • 可以通过为事件绑定响应函数(回调函数),来完成和用户之间的交互

  • 绑定响应函数的方式:

    1. 可以直接在元素的属性中设置

    2. 可以通过为元素的指定属性设置回调函数的形式来绑定事件(一个事件只能绑定一个响应函数)

    3. 可以通过元素addEventListener()方法来绑定事件

2.4.1 直接在元素属性中设置



2.4.2 设置回调函数


2.4.3 元素.addActionListener()


2.5 文档的加载

网页是自上向下加载的,如果将js代码编写到网页的上边,js代码在执行时,网页还没有加载完毕,这时会出现无法获取到DOM对象的情况



Title





上述代码在打印的时候就会打印出来null,表示无法获取到button元素

如果将script移动到button之后,就可以正常打印出结果



Title




为了保证在任意位置写的代码均生效我们可以使用下面两个函数

  • window.onload 事件会在窗口中的内容加载完毕之后才触发
  • document的DOMContentLoaded事件会在当前文档加载完毕之后触发

例如引入iframe之后,window.onload会在iframe全部加载完成之后才会执行,document会在当前文档加载完成之后执行,而不会等待另一个iframe加载完成之后才执行



Title





如何解决这个问题:

  1. 将script标签编写到body的最后

  2. 将代码编写到window.onload的回调函数中

  3. 将代码编写到document对象的DOMContentLoaded的回调函数中(执行时机更早)

  4. 将代码编写到外部的js文件中,然后以defer的形式进行引入(执行时机更早,早于DOMContentLoaded)

    
    

2.6 练习

2.6.1 切换图片

实现点击切换图片,并实现循环切换



Title



2.6.2 实现按钮之间相互配合

image-20221206210905757

  • 全选:选中所有按钮
  • 取消:取消选中所有按钮
  • 反选:选中则取消,取消则选中
  • 提交:显示出所有爱好
  • 如果下面所有标签都选中了则全选也需要被选中;如果有一个没有被选中,则全选不能被选中


Title


请选择你的爱好: 全选
乒乓球 篮球 羽毛球 足球

3. DOM的修改

3.1 添加节点

  • document.createElement(tagName):创建一个标签
  • document.appendChild():用于给一个节点添加子节点
  • document.insertAdjacentElement():可以向元素的任意位置添加元素
    • 两个参数:1.要添加的位置 2.要添加的元素
    • beforeend 标签的最后
    • afterbegin 标签的开始
    • beforebegin 在元素的前边插入元素(兄弟元素)
    • afterend 在元素的后边插入元素(兄弟元素)
  • docuemnt.insertAdjacentHTML():向任意位置添加HTML文本
// 创建一个li
const li = document.createElement("li")
// 向li中添加文本
li.textContent = "唐僧"
// 给li添加id属性
li.id = "ts"const list = document.getElementById("list")// 下面三种方法插入的位置都是一样的
list.appendChild(li)
// list.insertAdjacentElement("beforeend", li)
// list.insertAdjacentHTML("beforeend", "
  • 唐僧
  • ")

    3.2 删除节点

    • document.replaceWith():使用一个元素来替换当前元素
    • document.remove():删除当前元素
    const li = document.createElement("li")
    li.textContent = "蜘蛛精"
    li.id = "zzj"// 获取swk
    const swk = document.getElementById("swk")// replaceWith() 使用一个元素替换当前元素
    swk.replaceWith(li)// remove()方法用来删除当前元素
    // swk.remove()
    

    3.3 练习

    实现表格的增删

    • 删除:删除一整行数据
    • 增加:将下方填入信息加入表格中

    image-20221207130622822

    注意事项

    • a标签的跳转

      • 在a标签的href中设置为#,依然会发生跳转,#表示跳转到页面开头
      • 在a标签的onclick事件最后返回false就可以取消默认的跳转行为,但是这种方式只在 xxx.xxx = function(){}这种方式绑定的事件中才适用
      • 也可设置为javascript:;改为执行这段代码,也可以取消默认跳转
    • form中的button中type设置为button可以取消默认的提交事件

    • 这种写法,容易被xss的攻击,当用户在姓名地方写入

      3.4 复制节点

      • document.cloneNode():对节点进行复制时,它会复制节点的所有特点包括各种属性
        • 这个方法默认只会复制当前节点,而不会复制节点的子节点
        • 可以传递一个true作为参数,这样该方法也会将元素的子节点一起复制

      下面案例演示将一个li节点从一个list复制到另一个list中

      如果直接使用appendChild的话则id为l1的标签会在第一个ul中消失

      
      Document
      • 孙悟空
      • 猪八戒
      • 沙和尚
      • 蜘蛛精

      3.5 修改CSS样式

      • 通过元素.style.样式名 = 样式值修改element的样式
        • 会直接在元素的内联样式中添加对应样式
        • 如果样式名中含有-,则需要将样式表修改为驼峰命名法
        • background-color --> backgroundColor

      案例:点击按钮后,修改box1的宽度

      
      
      Document
      
      

      3.6 读取CSS样式

      3.6.1 getComputedStyle()

      元素.style.样式名读取到的是内联样式,如果是通过class定义的样式,则这种形式不能获取到样式

      正确的方式是使用getComputedStyle()来读取样式

      • 它会返回一个对象,这个对象中包含了当前元素所有的生效的样式

      • 参数:

        1. 要获取样式的对象
        2. 要获取的伪元素
      • 返回值:返回的一个对象,对象中存储了当前元素的样式

      • 注意:样式对象中返回的样式值,不一定能来拿来直接计算,所以使用时,一定要确保值是可以计算的才去计算,如果获取数值的话可以使用parseInt()处理,在处理完之后设置的时候记得再加回去

      案例:点击按钮后,获取box的样式

      
      Document

      3.6.2 通过属性读取样式

      • 获取元素内部的宽度和高度(包括内容区和内边距),返回的是数值
        • 元素.clientHeight:height + 2*padding
        • 元素.clientWidth:width + 2*padding
      • 获取元素的可见框的大小(包括内容区、内边距和边框
        • 元素.offsetHeight:height + 2*padding + border
        • 元素.offsetWidth:width + 2*padding + border
      • 获取元素滚动区域的大小(包括可见和不可见的总大小)
        • 元素.scrollHeight:内容区最大高度 + 内边距
        • 元素.scrollWidth:内容区最大宽度 + 内边距
      • 获取元素的定位父元素
        • 元素.offsetParent
        • 定位父元素:离当前元素最近的开启了定位的祖先元素,如果所有的元素都没有开启定位则返回body
      • 获取元素相对于其定位父元素的偏移,所有父元素都没有开启定位的话则是相对于body
        • 元素.offsetTop
        • 元素.offsetLeft
      • 获取或设置元素滚动条的偏移量(可以修改的)
        • 元素.scrollTop
        • 元素.scrollLeft
      
      
      Document
      
      

      3.7 修改class

      如果直接通过js代码中修改某个样式,会造成代码耦合太高,我们可以通过修改class属性来间接的修改样式

      通过class修改样式的好处:

      1. 可以一次性修改多个样式
      2. 对JS和CSS进行解耦
      const box1 = document.querySelector(".box1")box1.className += " box2"
      

      元素.classList 是一个对象,对象中提供了对当前元素的类的各种操作方法

      • 元素.classList.add() 向元素中添加一个或多个class,如果有的话则不会做任何操作
      • 元素.classList.remove() 移除元素中的一个或多个class
      • 元素.classList.toggle() 切换元素中的class
      • 元素.classList.replace() 替换class
      • 元素.classList.contains() 检查class

      案例:点击按钮后,修改box1的宽度

      
      
      Document
      
      

      4. 事件

      4.1 事件对象简介

      • 事件对象是由浏览器在事件触发时所创建的对象,这个对象中封装了事件相关的各种信息
      • 通过事件对象可以获取到事件的详细信息比如:鼠标的坐标、键盘的按键…
      • 浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象

      案例:获取鼠标的坐标

      const box1 = document.getElementById("box1")// 下面两种方式都可以拿到事件对象
      // box1.onmousemove = event => {
      //     console.log(event)
      // }box1.addEventListener("mousemove", event => {console.log(event.clientX, event.clientY)box1.textContent = event.clientX + "," + event.clientY
      })
      

      4.2 Event对象

      Event - Web API 接口参考

      • 在DOM中存在着多种不同类型的事件对象,多种事件对象有一个共同的祖先 Event
        • event.target 触发事件的对象
        • event.currentTarget 绑定事件的对象(同this)
        • event.stopPropagation() 停止事件的传导
        • event.preventDefault() 取消默认行为
      • 事件的冒泡(bubble)
        • 事件的冒泡就是指事件的向上传导
        • 当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发
        • 冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在不希望事件冒泡时,可以通过事件对象来取消冒泡

      案例:阻止事件冒泡

      • 点击最小的div不会向外冒泡,点击中间的div会向外冒泡

      image-20221207145636903

      
      
      Document
      
      

      案例:取消超链接的默认跳转行为

      const link = document.querySelector("a")link.addEventListener("click", (event) => {event.preventDefault() // 取消默认行为alert("被点了~~~")})
      

      4.3 冒泡

      • 取消冒泡:event.stopPropagation()

      案例:图标跟随鼠标

      
      
      Title
      
      

      案例:在一片区域中图标不跟随鼠标(使用阻止冒泡事件实现效果)

      
      
      Title
      
      

      4.4 事件的委派

      委派就是将本该绑定给多个元素的事件,统一绑定给父级元素,这样可以降低代码复杂度方便维护

      案例:点击li标签,打印出其中的内容

      思路:原来是通过在每一个li上都绑定一个点击事件,然后对新加的li也再添加事件,但是现在可以使用事件的委派来实现这个操作,也就是直接绑定在父级元素上

      
      
      Title
      
      
      • 链接1
      • 链接2
      • 链接3
      • 链接4

      4.5 事件的捕获

      事件的传播机制

      • 在DOM中,事件的传播可以分为三个阶段:
        1. 捕获阶段 (由祖先元素向目标元素进行事件的捕获)(默认情况下,事件不会在捕获阶段触发
        2. 目标阶段 (触发事件的对象)
        3. 冒泡阶段 (由目标元素向祖先元素进行事件的冒泡)
      • 事件的捕获,指事件从外向内的传导,当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获
      • 如果希望在捕获阶段触发事件,可以将addEventListener的第三个参数设置为true
        • 一般情况下我们不希望事件在捕获阶段触发,所有通常都不需要设置第三个参数

      通过event.eventPhase可以获得事件触发的阶段

      1 捕获阶段 2 目标阶段 3 冒泡阶段
      
      • 捕获:由外向里传播
      • 冒泡:由里向外传播
      • 使用stopPropagation()可以停止捕获或者冒泡,在执行到的函数停止
      
      Document

      5. 其他

      5.1 定时器

      通过定时器,可以使代码在指定时间后执行

      5.1.1 单次定时器

      setTimeout()

      • 参数:
        1. 回调函数(要执行的代码)
        2. 间隔的时间(毫秒)
      • 关闭定时器:clearTimeout(标识)
      const timer = setTimeout(()=>{alert("我是定时器中的代码")
      }, 3000)clearTimeout(timer)
      

      5.1.2 多次定时器

      setInterval() (每间隔一段时间代码就会执行一次)

      • 参数:
        1. 回调函数(要执行的代码)
        2. 间隔的时间(毫秒)
      • 关闭定时器:clearInterval()
      let num = 0const timer = setInterval(() => {num++numH1.textContent = numif(num === 200){clearInterval(timer)}
      }, 30)
      

      5.1.3 原理

      定时器的原理是在到时之后将定时器的回调函数放到消息队列中,等调用栈中的函数执行完成之后才去执行

      通过以下代码就可以看到,定时器并不是3000ms倒计时结束就立即执行的

      console.time()
      setTimeout(function(){console.timeEnd()console.log("定时器执行了~")
      }, 3000)使程序卡6s
      const begin = Date.now()
      while (Date.now() - begin < 6000) {}
      

      image-20221207223252289

      setInterval()是每隔固定时间将函数放到消息队列汇总,如果函数的执行速度比较慢,则无法确保每一次的执行间隔是一样的,通过下面这种方式可以确保每次执行都有相同的间隔

      console.time("间隔")
      setTimeout(function fn() {console.timeEnd("间隔")alert("哈哈")console.time("间隔")// 在setTimeout的回调函数的最后,在调用一个setTimeoutsetTimeout(fn, 3000)
      }, 3000)
      

      5.2 事件循环

      事件循环(event loop)

      • 函数在每次执行时,都会产生一个执行环境
      • 执行环境负责存储函数执行时产生的一切数据
      • 问题:函数的执行环境要存储到哪里呢?
        • 函数的执行环境存储到了一个叫做调用栈的地方
        • 栈,是一种数据结构,特点 后进先出
      • 调用栈(call stack)
        • 调用栈负责存储函数的执行环境
        • 当一个函数被调用时,它的执行环境会作为一个栈帧,插入到调用栈的栈顶,函数执行完毕其栈帧会自动从栈中弹出

      对于下列代码,执行到fn2()函数内部时调用栈如图,当执行玩fn2()之后会将fn2对应栈帧从调用栈中弹出,直到执行完所有代码

      image-20221207221447471

      5.3 消息队列

      • 消息队列负责存储将要执行的函数
      • 当我们触发一个事件时,其响应函数并不是直接就添加到调用栈中的,因为调用栈中有可能会存在一些还没有执行完的代码
      • 事件触发后,JS引擎是将事件响应函数插入到消息队列中排队

      当我们点击页面中的按钮的时候,对应的点击事件会先加入到消息队列中,等待调用栈中的所有代码全部执行完了之后,再从消息队列中取出第一个事件到调用栈中执行,直到消息队列为空,然后后面会定期扫描消息队列,如果消息队列中有新消息,则会继续执行

      image-20221207222200446

      调用栈先于消息队列执行的证据:

      下面这段代码,setTimeout() 0ms后就应该执行,说明就是立即执行的,但是实际运行结果却是2222先打印

      setTimeout(() => {console.log(11111)
      }, 0)console.log(222222)
      

      image-20221207224146590

    相关内容

    热门资讯

    银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
    【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
    AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
    不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
    AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
    Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
    月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
    ​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
    北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
    AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...