Skip to content

DOM ​

DOM 即 Document Object Model 文档对象模型,浏览器在加载页面是会生成 DOM 对象,以供我们使用 JS 控制页面元素。

文档渲染 ​

浏览器会将 HTML 文本内容进行渲染,并生成相应的 JS 对象,同时会对不符规则的标签进行处理。

  • 浏览器会将标签规范后渲染页面
  • 目的一让页面可以正确呈现
  • 目的二可以生成统一的 JS 可操作对象

操作时机 ​

需要保证浏览器已经渲染了内容才可以读取的节点对象,下例将无法读取到节点对象

javascript
<script>
  const node = document.getElementById('love')
  console.log(node) //null
</script>
<h1 id="love">love</h1>

不过我们可以将脚本通过事件放在页面渲染完执行

javascript
<script>
  window.onload = () => {
    const node = document.getElementById('love')
    console.log(node)
  }
</script>
<h1 id="love">love</h1>

或使用定时器将脚本设置为异步执行

javascript
<script>
  setTimeout(() => {
    const node = document.getElementById('love')
    console.log(node)
  })
</script>
<h1 id="love">love</h1>

或将脚本设置在外部文件并使用 defer 属性加载,defer 即会等到 DOM 解析后迟延执行

javascript
<script defer="defer" src="love.js"></script>
<div id="love"></div>

节点对象 ​

JS 中操作 DOM 的内容称为节点对象(node),即然是对象就包括操作 NODE 的属性和方法

  • 包括 12 种类型的节点对象
  • 常用了节点为 document、标签元素节点、文本节点、注释节点
  • 节点均继承自 Node 类型,所以拥有相同的属性或方法
  • document 是 DOM 操作的起始节点
节点nodeTypenodeNamenodeValue示例
元素节点(Element Nodes)1大写标签名不包含值或返回null‌ <div>、<p>、<a>
属性节点(Attribute Nodes)2属性的名称属性的值‌id href class
文本节点(Text Nodes)3#text内容
CDATA区段节点(CDATASection Nodes)4仅存在XML文档中
注释节点(Comment Nodes)8#comment注释的内容‌
文档节点(Document Nodes)9#documentnull
文档类型节点10doctype的名称null<!DOCTYPE html>

原型链 ​

原型说明
Object根对象,提供 hasOwnProperty 等基本对象操作支持
EventTarget提供 addEventListener、removeEventListener 等事件支持方法
Node提供 firstChild、parentNode 等节点操作方法
Element提供 getElementsByTagName、querySelector 等方法
HTMLElement所有元素的基础类,提供 childNodes、nodeType、nodeName、className、nodeName 等方法
HTMLHeadingElementHead 标题元素类

打印原型链

js
function prototype(el) {
    const prototypes = []
    prototypes.push(el.__proto__)
    prototypes.push(...(el.__proto__ ? prototype(el.__proto__) : []))
    return prototypes
}

对象特征 ​

利用Object.assign添加或者修改属性。

常用节点 ​

JS 提供了访问常用节点的 api

方法说明
documentdocument 是 DOM 操作的起始节点
document.documentElement文档节点即 html 标签节点
document.bodybody 标签节点
document.headhead 标签节点
document.links超链接集合
document.anchors所有锚点集合
document.formsform 表单集合
document.images图片集合

DOCUMENT ​

document 是 window 对象的属性,是由 HTMLDocument 类实现的实例。

节点属性 ​

使用 data 属性可以获取文本与注释内容

javascript
<div id="app">
  love.com
  <!-- love 注释内容-->
</div>

<script>
  const app = document.querySelector('#app')
  console.log(app.childNodes[0].data)
  console.log(app.childNodes[1].data)
</script>

节点集合 ​

Nodelist 与 HTMLCollection 都是包含多个节点标签的集合,大部分功能也是相同的。

  • getElementsByClassName 等方法返回的是 NodeList
  • querySelectorAll 返回的是 HTMLCollection
  • NodeList 节点列表是动态的,即内容添加后会动态更新

item ​

Nodelist 与 HTMLCollection 提供了 item()方法来根据索引获取元素,也可以使用数组索引。

javascript
<script>
  const nodes = document.getElementsByTagName('div')
  console.dir(nodes.item(0))
</script>

<script>
  const nodes = document.getElementsByTagName('div')
  console.dir(nodes[0])
</script>

namedItem ​

HTMLCollection 具有 namedItem 方法可以按 name 或 id 属性来获取元素

html
<div name="app">
  <div id="love">houdunren.com</div>
  <div name="you">houdunwang.com</div>
</div>

<script>
  const nodes = document.getElementsByTagName('div')
  console.dir(nodes.namedItem('love'))
   console.dir(nodes.namedItem('you'))
</script>

数字索引时使用 item 方法,字符串索引时使用 namedItem 或 items 方法

动态与静态 ​

通过 getElementsByTagname 等 getElementsBy... 函数获取的 Nodelist 与 HTMLCollection 集合是动态的,即有元素添加或移动操作将实时反映最新状态。

  • 使用 getElement...返回的都是动态的集合
  • 使用 querySelectorAll 返回的是静态集合

动态特性 ​

下例中通过按钮动态添加元素后,获取的元素集合是动态的,而不是上次获取的固定快照。

html
<h1>baidu.com</h1>
<h1>jd.com</h1>
<button id="add">添加元素</button>

<script>
  let elements = document.getElementsByTagName('h1')
  console.log(elements)
  let button = document.querySelector('#add')
  button.addEventListener('click', () => {
    document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>love you</h1>')
    console.log(elements)
  })
</script>

document.querySelectorAll 获取的集合是静态的

遍历节点 ​

forOf ​

Nodelist 与 HTMLCollection 是类数组的可迭代对象所以可以使用 for...of 进行遍历

forEach ​

Nodelist 节点列表也可以使用 forEach 来进行遍历,但 HTMLCollection 则不可以

call/apply ​

节点集合对象原型中不存在 map 方法,但可以借用 Array 的原型 map 方法实现遍历

javascript
Array.prototype.map.call(nodes, (node, index) => {
    console.log(node, index)
})

Array.from ​

Array.from 用于将类数组转为组件,并提供第二个迭代函数。所以可以借用 Array.from 实现遍历

javascript
 Array.from(nodes, (node, index) => {
    console.log(node, index)
})

节点关系 ​

下面是通过节点关系获取相应元素的方法

节点属性说明
childNodes获取所有子节点
parentNode获取父节点
firstChild第一个子节点
lastChild最后一个子节点
nextSibling下一个兄弟节点
previousSibling上一个兄弟节点

标签关系 ​

使用 childNodes 等获取的节点包括文本与注释,但这不是我们常用的,为此系统也提供了只操作元素的关系方法。

基础知识 ​

下面是处理标签关系的常用 API

节点属性说明
parentElement获取父元素
children获取所有子元素
childElementCount子标签元素的数量
firstElementChild第一个子标签
lastElementChild最后一个子标签
previousElementSibling上一个兄弟标签
nextElementSibling下一个兄弟标签
contains返回布尔值,判断传入的节点是否为该节点的后代节点

标签获取 ​

getElementById ​

使用 ID 选择是非常方便的选择具有 ID 值的节点元素,但注意 ID 应该是唯一的,getElementById 只能通过 document 访问。

getElementsByName ​

使用 getElementByName 获取设置了 name 属性的元素。

  • 返回 NodeList 节点列表对象
  • NodeList 顺序为元素在文档中的顺序
  • 需要在 document 对象上使用

getElementsByTagName ​

使用 getElementsByTagName 用于按标签名获取元素

  • 返回 HTMLCollection 节点列表对象
  • 是不区分大小的获取

getElementsByClassName ​

getElementsByClassName 用于按 class 样式属性值获取元素集合

  • 设置多个值时顺序无关,指包含这些 class 属性的元素

样式选择器 ​

querySelectorAll ​

使用 querySelectorAll 根据 CSS 选择器获取 Nodelist 节点列表

  • 获取的 NodeList 节点列表是静态的,添加或删除元素后不变

querySelector ​

querySelector 使用 CSS 选择器获取一个元素。

matches ​

用于检测元素是否是指定的样式选择器匹配

closest ​

查找最近的符合选择器的祖先元素(包括自身)

标准属性 ​

属性别名 ​

有些属性名与 JS 关键词冲突,系统已经起了别名

属性别名
classclassName
forhtmlFor

元素特征 ​

对于标准的属性可以使用 DOM 属性的方式进行操作,但对于标签的非标准的定制属性则不可以。但 JS 提供了方法来控制标准或非标准的属性

可以理解为元素的属性分两个地方保存,DOM 属性中记录标准属性,特征中记录标准和定制属性

  • 使用特征操作时属性名称不区分大小写
  • 特征值都为字符串类型
方法说明
getAttribute获取属性
setAttribute设置属性
removeAttribute删除属性
hasAttribute属性检测

特征是可迭代对象,下面使用 for...of 来进行遍历操作

attributes ​

元素提供了 attributes 属性可以只读的获取元素的属性

html
<div class="love" data-content="静思">love.com</div>
<script>
  let houdunwang = document.querySelector('.love')
  console.dir(houdunwang.attributes['data-content'].nodeValue) //静思
	console.dir(houdunwang.attributes['data-content'].value) //静思
</script>

自定义特征 ​

虽然可以随意定义特征并使用 getAttribute 等方法管理,但很容易造成与标签的现在或未来属性重名。建议使用以 data-为前缀的自定义特征处理,针对这种定义方式 JS 也提供了接口方便操作。

  • 元素中以 data-为前缀的属性会添加到属性集中
  • 使用元素的 dataset 可获取属性集中的属性

属性同步 ​

下面对 input 值使用属性设置,但并没有同步到特征

html
<input type="text" name="package" value="love.com" />
<script>
  const package = document.querySelector(`[name='package']`)
  package.value = 'love.com'
  console.log(package.getAttribute('value'))//love.com
</script>

但改变 input 的特征 value 会同步到 DOM 对象属性

html
<input type="text" name="package" value="love.com" />
<script>
  const package = document.querySelector(`[name='package']`)
  package.setAttribute('value', 'love.com')
  console.log(package.value) //love.com
</script>

创建节点 ​

创建节点的就是构建出 DOM 对象,然后根据需要添加到其他节点中

createElement ​

使用 createElement 方法可以标签节点对象>

使用 PROMISE 结合节点操作来加载外部 JAVASCRIPT 文件

javascript
function js(file) {
  return new Promise((resolve, reject) => {
    let js = document.createElement('script')
    js.type = 'text/javascript'
    js.src = file
    js.onload = resolve
    js.onerror = reject
    document.head.appendChild(js)
  })
}

js('outside.js')
  .then(() => console.log('加载成功'))
  .catch((error) => console.log(`${error.target.src} 加载失败`))

cloneNode&importNode ​

使用 cloneNode 和 document.importNode 用于复制节点对象操作

  • cloneNode 是节点方法
  • cloneNode 参数为 true 时递归复制子节点即深拷贝
  • importNode 是 documet 对象方法

复制 div#app 节点并添加到 body 元素中

html
<div id="app">love</div>
<script>
  let app = document.querySelector('#app')
  let newApp = app.cloneNode(true)
  document.body.appendChild(newApp)
</script>

document.importNode 方法是部分 IE 浏览器不支持的,也是复制节点对象的方法

  • 第一个参数为节点对象
  • 第二个参数为 true 时递归复制
html
<div id="app">love</div>
<script>
  let app = document.querySelector('#app')
  let newApp = document.importNode(app, true)
  document.body.appendChild(newApp)
</script>

节点内容 ​

innerHTML ​

inneHTML 用于向标签中添加 html 内容,同时触发浏览器的解析器重绘 DOM。

  • innerHTML 中只解析 HTML 标签语法,所以其中的 script 不会做为 JS 处理

outerHTML ​

outerHTML 与 innerHTML 的区别是包含父标签

textContent 与 innerText ​

textContent 与 innerText 是访问或添加文本内容到元素中

  • textContentb 部分 IE 浏览器版本不支持
  • innerText 部分 FireFox 浏览器版本不支持
  • 获取时忽略所有标签,只获取文本内容
  • 设置时将内容中的标签当文本对待不进行标签解析

设置时将标签当文本对待,即转为 HTML 实体内容

html
<div id="app">
  <div class="love" data="hd">love.com</div>
</div>
<script>
  let app = document.querySelector('#app')
  app.textContent="<h1>love</h1>" //<h1>love</h1>
</script>

outerText ​

与 innerText 差别是会影响所操作的标签

insertAdjacentText ​

将文本插入到元素指定位置,不会对文本中的标签进行解析,包括以下位置

选项说明
beforebegin元素本身前面
afterend元素本身后面
afterbegin元素内部前面
beforeend元素内部后面

节点管理 ​

推荐方法 ​

方法说明
append节点尾部添加新节点或字符串
prepend节点开始添加新节点或字符串
before节点前面添加新节点或字符串
after节点后面添加新节点或字符串
replaceWith将节点替换为新节点或字符串

将 h2 移动到 h1 之前

html
<h1>first.com@h1</h1>
<h2>second@h2</h2>
<script>
  let h1 = document.querySelector('h1')
  let h2 = document.querySelector('h2')
  h1.before(h2)
</script>

insertAdjacentHTML ​

将 html 文本插入到元素指定位置,浏览器会对文本进行标签解析,包括以下位置

选项说明
beforebegin元素本身前面
afterend元素本身后面
afterbegin元素内部前面
beforeend元素内部后面

insertAdjacentElement ​

insertAdjacentElement() 方法将指定元素插入到元素的指定位置,包括以下位置

  • 第一个参数是位置
  • 第二个参数为新元素节点
选项说明
beforebegin元素本身前面
afterend元素本身后面
afterbegin元素内部前面
beforeend元素内部后面

两者区别就是插入类型不同,insertAdjacentElement插入的元素需要继承type 'Element'.

样式管理 ​

通过 DOM 修改样式可以通过更改元素的 class 属性或通过 style 对象设置行样式来完成。

  • 建议使用 class 控制样式,将任务交给 CSS 处理,更简单高效

classList ​

如果对类单独进行控制使用 classList 属性操作

方法说明
node.classList.add添加类名
node.classList.remove删除类名
node.classList.toggle切换类名
node.classList.contains类名检测

在元素的原有 class 上添加新 class

html
<div id="app" class="d-flex container">love</div>
<script>
  let app = document.getElementById('app')
  app.classList.add('love') //love为类名作为参数
</script>

使用 classList 也可以移除 class 列表中的部分 class

html
<div id="app" class="d-flex container">love</div>
<script>
  let app = document.getElementById('app')
  app.classList.remove('container')
</script>

使用 toggle 切换类,即类已经存在时删除,不存在时添加

html
<div id="app" class="d-flex container">love</div>
<script>
  let app = document.getElementById('app')
  app.addEventListener('click', function () {
    this.classList.toggle('love')
  })
</script>

使用 contains 检查 class 是否存在

html
<div id="app" class="d-flex container">love</div>
<script>
  let app = document.getElementById('app')
  console.log(app.classList.contains('container')) //true
  console.log(app.classList.contains('love')) //false
</script>

设置行样式 ​

使用 style 对象可以对样式属性单独设置,使用 cssText 可以批量设置行样式

样式属性设置

使用节点的 style 对象来设置行样式

js
app.style.backgroundColor = 'red'

批量设置行样式

使用 cssText 属性可以批量设置行样式,属性名和写 CSS 一样不需要考虑驼峰命名

js
app.style.cssText = `background-color:red;color:yellow`
app.setAttribute('style', `background-color:red;color:yellow;`)

获取样式 ​

可以通过 style 对象,window.getComputedStyle 对象获取样式属性,下面进行说明

style

可以使用 DOM 对象的 style 属性读取行样式

  • style 对象不能获取行样式外定义的样式
js
console.log(app.style.backgroundColor)
console.log(app.style.margin)
console.log(app.style.marginTop)
console.log(app.style.color)

getComputedStyle

使用 window.getComputedStyle 可获取所有应用在元素上的样式属性

  • 函数第一个参数为元素
  • 第二个参数为伪类
  • 这是计算后的样式属性,所以取得的单位和定义时的可能会有不同
html
<style>
    div {
        font-size: 35px;
        color: yellow;
    }
</style>
<div id="app" style="background-color: red; margin: 20px;">love</div>
<script>
    let app = document.getElementById('app')
    let style = window.getComputedStyle(app)
    console.log(style.fontSize.slice(0, -2))
    console.log(parseInt(style.fontSize))
    app.style.fontSize = parseInt(style.fontSize) + 21 + 'px'
    console.log(parseInt(style.fontSize))
</script>