DOM

基础知识

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

文档渲染

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

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

标签修复

  • 没有任何标签时,通过浏览器的 检查>元素 标签查看会自动修复
  • 标签结束错误并且属性也没有引号,浏览器在渲染中会进行修复

表格处理

  • 表格tabel中不允许有内容,浏览器在渲染过程中会进行处理

标签移动

所有内容要写在BODY标签中,SCRIPT标签写在了BODY后面,浏览器渲染后也会进行处理

操作时机

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

<script>
    const node = document.getElementById('mk')
    console.log(node)
</script>

<h1 id="mk">mkimq.com</h1>
// 将脚本通过事件放在页面渲染完执行
window.onload = function () {
    const node = document.getElementById('mk')
    console.log(node)
}

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

setTimeout(() => {
    const node = document.getElementById('mk')
    console.log(node)
})

// 或将脚本设置在外部文件并使用defer属性加载,defer即会等到DOM解析后迟延执行
<script defer="defer" src="mk.js"></script>

节点对象

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

  • 包括12种类型的节点对象
  • 常用了节点为document、标签元素节点、文本节点、注释节点
  • 节点均继承自Node类型,所以拥有相同的属性或方法
  • document是DOM操作的起始节点
<body id="mk">
    <!-- mkimq -->
</body>

<script>
    // document节点 noteType为9
    console.log(document.noteType)

    // 第一个子节点为<!DOCTYPE html>,且nodetype为10
    console.log(document.childNodes.item(0).nodeType)

    // body 是标签节点 nodeType为1
    console.log(document.body.nodeType)

    // body的属性节点 nodeType 为2
    console.log(document.body.attributes[0].nodeType)

    // body的第一个节点为文本节点,nodeType为3
    console.log(document.body.childNodes.item(0).nodeType)

    // body的第二个节点为注释,nodeType类型为8
    console.log(document.body.childNodes[1].nodeType)
</script>

原型链

在浏览器渲染过程中会将文档内容生成为不同的对象,下例中的h1标签进行讨论,其他节点情况相似

  • 不同类型节点由专有的构造函数创建对象
  • 使用console.dir 可以打印出DOM节点对象结构
  • 节点也是对象所以也具有JS对象的特征
<h1 id="mk">mkimq.com</h1>

<script>
    function prototype(el) {
        console.dir(el.__proto__)
        el.__proto__ ? prototype(el.__proto__) : ''
    }

    const node = document.getElementById('mk')
    prototype(node)
</script>

最终得到的节点的原型链为

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

将上面的方法优化一下,实现提取节点原型链的数组

<h2 id="h2 value">mkimq.com</h2>
<input type="text" id="inputId" value="mkimq" />
<script>
    function prototype(el) {
        const prototypes = []
        prototypes.push(el.__proto__)
        prototypes.push(...(el.__proto__ ? prototype(el.__proto__) : []))
        return prototypes
    }
    const h2 = document.querySelector('h2')
    const input = document.querySelector('input')

    console.log(prototype(input))
</script>

标题元素增加两个原型方法,改变颜色与隐藏元素

<h2 onclick="this.color('red')">mkimq.com</h2>
<script>
    const h2 = document.querySelector('h2')
    HTMLHeadingElement.prototype = Object.assign(HTMLHeadingElement.prototype, {
        color(color) {
            this.style.color = color
        },
        hide() {
            this.style.display = 'none'
        },
    })
</script>

对象特征

即然DOM与我们其他JS创建的对象特征相仿,所以也可以为DOM对象添加属性或方法。

对于系统应用的属性,应该明确含义不应该随意使用,比如ID是用于标识元素唯一属性,不能用于其他目地

ID属性可以直接修改但是不建议这么做

<h2 id="mk">mkimq.com</h2>
<script>
    const h2 = document.getElementById('mk')
    h2.id = 'mkimq.com'
    console.log(h2)
</script>

title用于显示提示文档也不应该用于其他目地

<h2 id="mk">mkimq.com</h2>
<script>
    const h2 = document.getElementById('mk')
    h2.title = 'mkimq.com'
    console.log(h2)
</script>

对象合并属性的示例

<h2 id="mk">mkimq.com</h2>
<script>
    const h2 = document.getElementById('mk')

    Object.assign(h2, {
        // 设置内容
        innerHTML: 'mankeung',
        color: 'red',
        change() {
            this.innerHTML = 'mkimq'
            this.style.color = this.color
        },
        onclick() {
            this.change()
        }
    })
</script>

使用对象特性更改样式属性

<h2 id="mk">mkimq.com</h2>
<script>
    const h2 = document.getElementById('mk')

    Object.assign(h2.style, {
        color: 'white',
        backgroundColor: 'red'
    })
</script>

常用节点

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

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

DOCUMENT

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

  • document包含 DocumentType(唯一)或 html元素(唯一)或 comment等元素

原型链中也包含Node,所以可以使用有关节点操作的方法如nodeType/NodeName等

console.dir(document.nodeType)
console.dir(document.nodeName)

使用title获取和设置文档标题

// 获取文档标题
console.log(document.title)

// 设置文档标签
document.title = 'mkimq.com'

获取当前URL

console.log(document.URL)

获取域名

console.log(document.domain)

获取来源地址

console.log(document.referrer)

ID

下面是直接使用 ID 获取元素(这是非标准操作,对浏览器有挑剔)

<h2 id="mk">mkimq.com</h2>
<script>
    // 直接通过 ID 获取元素(非标准操作)
    console.dir(mk)
</script>

下面展示的是获取所有a标签

<h2 id="mk">
    <a href="">mkimq.com</a>
    <a href="">mkimq.com</a>
</h2>
<script>
    const nodes = document.links
    console.log(nodes)
</script>

anchors

下例是获取锚点集合后能过 锚点 name 属性获取元素

<h2 id="mk">
    <a href="" name="n1">mkimq.com</a>
    <a href="" name="n2">mkimq.com</a>
</h2>
<script>
    // 通过锚点获取元素
    console.dir(document.anchors.n2)
</script>

images

下面是获取所有图片节点

<img src="" alt="" />
<img src="" alt="" />
<img src="" alt="" />
<script>
    // 获取所有图片节点
    console.dir(document.images)
</script>

节点属性

不同类型的节点拥有不同属性,下面是节点属性的说明与示例

nodeType

nodeType指以数值返回节点类型

nodeType说明
1元素节点
2属性节点
3文本节点
8注释节点
9document对象

下面是节点nodeType的示例

<div id="app">
    <div class="mkimq" data="id">mkimq.com</div>
    <div class="mk">mankeung.com</div>
    <div class="my"><!-- my --></div>
</div>

<script>
const node = document.querySelector('#app')
console.log(node.nodeType) // 1
console.log(node.firstChild.nodeType) // 3
console.log(node.attributes.id.nodeType) // 2

const m = document.querySelector('.my')
console.log(m.childNodes[0].nodeType) // 8
</script>

Prototype

当然也可以使用对象的原型进行检测

  • section 、main、aslide 标签的原型对象为HTMLElement
  • 其他非系统标签的原型对象为HTMLUnknownElement
let h1 = document.querySelector('h1')
let p = document.querySelector('p')
console.log(h1 instanceof HTMLHeadingElement) // true
console.log(p instanceof HTMLHeadingElement) // false
console.log(p instanceof Element) // true

nodeName

nodeName指定节点的名称

获取值为大写形式

nodeTypenodeName
1元素名称如DIV
2属性名称
3#text
8#comment
<div id="app">
    <div class="mkimq" data="id">mkimq.com</div>
    <div class="mk">mankeung.com</div>
    <div class="my"><!-- my --></div>
    <span>在线文档</span>
</div>

<script>
const node = document.querySelector('#app')
const span = document.querySelector('span')

// 标签节点为大写的标签名DIV
console.log(node.nodeName)
console.log(span.nodeName)

// 文本节点为 #text
console.log(node.firstChild.nodeName)

// 属性节点为属性名
console.log(node.attributes.id.nodeName)

// 注释节点为#comment
const m = document.querySelector('.my')
console.log(m.childNodes[0].nodeName)
</script>

tagName

nodeName可以获取不限于元素的节点名,tagName仅能用于获取标签节点的名称

  • tagName存在于Element类的原型中
  • 文本、注释节点值为 undefined
  • 获取的值为大写的标签名
<div id="app">
    <div class="mkimq" data="id">mkimq.com</div>
    <div class="mk">mankeung.com</div>
    <div class="my"><!-- my --></div>
    <span>在线文档</span>
</div>

<script>
const node = document.querySelector('#app')
const span = document.querySelector('span')

// 标签节点为大写的标签名DIV
console.log(node.tagName)
console.log(span.tagName)

// 文本节点为 #text
console.log(node.firstChild.tagName)

// 属性节点为属性名
console.log(node.attributes.id.tagName)

// 注释节点为#comment
const m = document.querySelector('.my')
console.log(m.childNodes[0].tagName)
</script>

nodeValue

使用nodeValue或data函数获取节点值,也可以使用节点的data属性获取节点内容

nodeTypenodeValue
1null
2属性值
3文本内容
8注释内容
<div id="app">
    <div class="mkimq" data="id">mkimq.com</div>
    <div class="mk">mankeung.com</div>
    <div class="my"><!-- my --></div>
    <span>在线文档</span>
</div>

<script>
const node = document.querySelector('#app')
const span = document.querySelector('span')

console.log(node.nodeValue)

console.log(span.firstChild.nodeValue)

console.log(node.attributes.id.nodeValue)

const m = document.querySelector('.my')
console.log(m.childNodes[0].nodeValue)
</script>

树状节点

<div id="app">
    <ul>
        <li><span></span><span></span></li>
        <li><span></span><span></span></li>
        <li><span></span><span></span></li>
    </ul>
</div>

<script>
function tree(el) {
    return Array.from(el.childNodes)
        .filter(node => node.tagName)
        .map(node => ({
            name: node.tagName,
            children: tree(node)
        }))
}

console.log(tree(document.getElementById('app')))
</script>

节点集合

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

  • getElementsBy...等方法返回的是HTMLCollection
  • querySelectorAll 返回的是 NodeList
  • NodeList节点列表是动态的,即内容添加后会动态更新
<div></div>
<div></div>

<script>
    // 结果为NodeList
    console.log(document.querySelectorAll('div'))

    // 结果为HTMLCollection
    console.log(document.getElementsByTagName('div'))
</script>

length

Nodelist与HTMLCollection包含length属性,记录了节点元素的数量

<div id="app">
    <div id="mkimq">mkimq.com</div>
    <div name="mk">mankeung.com</div>
</div>

<script>
    const nodes = document.getElementsByTagName('div')

    for (let i = 0; i < nodes.length; i++) {
        console.log(nodes[i])
    }
</script>

item

Nodelist与HTMLCollection提供了item()方法来根据索引获取元素

<div id="app">
    <div id="mkimq">mkimq.com</div>
    <div name="mk">mankeung.com</div>
</div>

<script>
    const nodes = document.getElementsByTagName('div')

    console.dir(nodes.item(0))
    // 使用数组索引获取更方便
    console.dir(nodes[0])
</script>

namedItem

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

<div id="app">
    <div id="mkimq">mkimq.com</div>
    <div name="mk">mankeung.com</div>
</div>

<script>
    const nodes = document.getElementsByTagName('div')

    console.dir(nodes.namedItem('mk'))
    console.dir(nodes.namedItem('mkimq'))

    // 也可以使用数组或属性方式获取
    console.dir(nodes['mk'])
    console.dir(nodes['mkimq'])

    // 数字索引时使用item方法,字符串索引时使用namedItem或 items方法
    console.log(nodes[0])
    console.log(nodes['mk'])
</script>

动态与静态

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

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

动态特性

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

<h1>mkimq.com</h1>
<h1>mankeung.com</h1>
<button id="add">添加元素</button>

<script>
    const elements = document.getElementsByTagName('h1')
    console.log(elements)

    const button = document.querySelector('#add')

    button.addEventListener('click', () => {
        document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>my</h1>')

        console.log(elements)
    })
</script>

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

<h1>mkimq.com</h1>
<h1>mankeung.com</h1>
<button id="add">添加元素</button>

<script>
    const elements = document.querySelectorAll('h1')
    console.log(elements)

    const button = document.querySelector('#add')

    button.addEventListener('click', () => {
        document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>my</h1>')

        console.log(elements)
    })
</script>

使用静态

如果需要保存静态集合,则需要对集合进行复制

<h1>mkimq.com</h1>
<h1>mankeung.com</h1>
<button id="add">添加元素</button>

<script>
    const elements = document.getElementsByTagName('h1')
    console.log(elements)
    const clone = Array.prototype.slice.call(elements)

    const button = document.querySelector('#add')

    button.addEventListener('click', () => {
        document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>my</h1>')

        console.log(elements)
        console.log(clone)
    })
</script>

遍历节点

forOf

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

<h1>mkimq.com</h1>
<h1>mankeung.com</h1>

<script>
    const elements = document.getElementsByTagName('h1')

    for (const el of elements) {
        console.log(el)
    }
</script>

forEach

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

const elements = document.querySelectorAll('h1')

elements.forEach(el => {
    console.log(el)
})

call/apply

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

const elements = document.querySelectorAll('h1')

Array.prototype.map.call(elements, (el, index) => {
    console.log(el, index)
})

;[].filter.call(elements, el => {
    console.log(el)
})

Array.from

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

Array.from(elements, (el, index) => {
    console.log(el, index)
})

展开语法

下面使用点语法转换节点为数组

;[...elements].map(item => {
    console.log(item)
})

节点关系

节点是父子级嵌套与前后兄弟关系,使用DOM提供的API可以获取这种关系的元素。

  • 文本和注释也是节点,所以也在匹配结果中

基础知识

节点是根据HTML内容产生的,所以也存在父子、兄弟、祖先、后代等节点关系,下例中的代码就会产生这种多重关系

  • h1与ul是兄弟关系
  • span与li是父子关系
  • ul与span是后代关系
  • span与ul是祖先关系
<h1>mkimq</h1>
<ul>
    <li>
        <span>mkimq.com</span>
        <strong>mankeung</strong>
    </li>
</ul>

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

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

父节点集合

查找元素的所有父节点

<div>
    <ul>
        <li><span></span></li>
    </ul>
</div>

<script>
    function parentNodes(node) {
        const nodes = []

        while ((node = node.parentNode)) nodes.push(node)

        return nodes
    }

    const span = document.querySelector('span')
    const nodes = parentNodes(span)
    console.log(nodes)
</script>

使用递归获取所有父级节点

function parentNodes(node) {
    const nodes = new Array(node.parentNode)

    if (node.parentNode) nodes.push(...parentNodes(node.parentNode))

    return nodes
}

const span = document.querySelector('span')
const nodes = parentNodes(span)
console.log(nodes)

后代节点集合

获取所有的后代元素SPAN的内容

<div id="app">
    <span>mkimq.com</span>
    <h2><span>mkimq</span></h2>
</div>

<script>
    function getChildNodeByName(el, name) {
        const items = []
        Array.from(el.children).forEach(node => {
            if (node.tagName == name.toUpperCase()) items.push(node)
            items.push(...getChildNodeByName(node, name))
        })

        return items
    }

    const nodes = getChildNodeByName(document, 'span')
    console.log(nodes)
</script>

标签关系

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

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

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

标签获取

  1. getElementById

使用ID选择是非常方便的选择具有ID值的节点元素,但注意ID应该是唯一的

  1. getElementsByName

使用getElementByName获取设置了name属性的元素,虽然在DIV等元素上同样有效,但一般用来对表单元素进行操作时使用。

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

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

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

可以使用通配符 * 获取所有元素 document.getElementsByTagName('*')

  1. getElementsByClassName

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

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

样式选择器

在CSS中可以通过样式选择器修饰元素样式,在DOM操作中也可以使用这种方式查找元素。使用过jQuery库的朋友,应该对这种选择方式印象深刻。

使用getElementsByTagName等方式选择元素不够灵活,建议使用下面的样式选择器操作,更加方便灵活

  1. querySelectorAll

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

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

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

  1. matches

用于检测元素是否是指定的样式选择器匹配,下面过滤掉所有name属性的LI元素

<div id="app">
    <li>mkimq</li>
    <li>mkimq.com</li>
    <li name="my">mankeung.com</li>
</div>

<script>
    const nodes = [...document.querySelectorAll('li')].filter(node => {
        return !node.matches('[name]')
    })

    console.log(nodes)
</script>
  1. closest

查找最近的符合选择器的祖先元素(包括自身),下例查找父级拥有 .comment类的元素

<div class="comment">
    <ul class="comment">
        <li>mkimq.com</li>
    </ul>
</div>

<script>
    const li = document.getElementsByTagName('li')[0]
    const node = li.closest(`.comment`)
    // 结果为 ul.comment
    console.log(node)
</script>

标准属性

元素的标准属性具有相对应的DOM对象属性

  • 操作属性区分大小写
  • 多个单词属性命名规则为第一个单词小写,其他单词大写
  • 属性值是多类型并不全是字符串,也可能是对象等
  • 事件处理程序属性值为函数
  • style属性为CSSStyleDeclaration对象
  • DOM对象不同生成的属性也不同

属性别名

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

属性别名
classclassName
forhtmlFor

操作属性

// 直接设置元素的className
app.className = 'mkimq'

// 设置图像元素的标准属性
img.src = 'https://www.mkimq.com/avatar.png'
img.alt = 'mkimq'

// hidden隐藏元
app.hidden = true

元素特征

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

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

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

attributes

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

<div class="mkimq" data-content="在线文档">mkimq.com</div>

<script>
    const node = document.querySelector('.mkimq')
    console.log(node.attributes['class'].nodeValue)
    console.log(node.attributes['data-content'].nodeValue)
</script>

自定义特征

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

  • 元素中以data-为前缀的属性会添加到属性集中
  • 使用元素的dataset可获取属性集中的属性
  • 改变dataset的值也会影响到元素上
<div class="mkimq" data-content="在线文档" data-title-txt="666" data-color="red">mkimq.com</div>

<script>
    const node = document.querySelector('.mkimq')
    const content = node.dataset.content
    const txt = node.dataset.titleColor
    console.log(content)
    console.log(txt)
    node.innerHTML = `<span style="color: ${node.dataset.color}">${content}</span>`
</script>

属性同步

特征和属性是记录元素属性的两个不同场所,大部分更改会进行同步操作。

创建节点

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

append

append 也是用于添加元素,同时他也可以直接添加文本等内容。

document.body.append((document.createElement('div').innerHTML = 'mkimq'))
document.body.append('mkimq.com')

createTextNode

创建文本对象并添加到元素中

const text = document.createTextNode('mkimq')
document.body.append(text)

createElement

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

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

cloneNode&importNode

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

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

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

const app = document.querySelector('#app')
const newApp = app.cloneNode(true)
document.body.appendChild(newApp)

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

  • 第一个参数为节点对象
  • 第二个参数为true时递归复制
const newApp = document.importNode(app, true)
document.body.appendChild(newApp)

节点内容

innerHTML

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

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

重绘节点

使用innertHTML操作会重绘元素,下面在点击第二次就没有效果了

  • 因为对#app内容进行了重绘,即删除原内容然后设置新内容
  • 重绘后产生的button对象没有事件
  • 重绘后又产生了新img对象,所以在控制台中可看到新图片在加载
<div id="app">
    <button>mkimq.com</button>
    <img src="1.jpg" alt="" />
</div>
<script>
    const app = document.querySelector('#app')
    app.querySelector('button').addEventListener('click', function () {
        alert(this.innerHTML)
        this.parentElement.innerHTML += '<hr/>在线文档'
    })
</script>

outerHTML

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

  • outerHTML不会删除原来的旧元素
  • 只是用新内容替换替换旧内容,旧内容(元素)依然存在

textContent与innerText

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

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

outerText

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

insertAdjacentText

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

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

节点管理

现在我们来讨论下节点元素的管理,包括添加、删除、替换等操作

推荐方法

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

remove方法可以删除节点 app.remove()

insertAdjacentHTML

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

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

insertAdjacentElement

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

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

古老方法

下面列表过去使用的操作节点的方法,现在不建议使用了。但在阅读老代码时可来此查看语法

方法说明
appendChild添加节点
insertBefore用于插入元素到另一个元素的前面
removeChild删除节点
replaceChild进行节点的替换操作

DocumentFragment

当对节点进行添加、删除等操作时,都会引起页面回流来重新渲染页面,即重新渲染颜色,尺寸,大小、位置等等。所以会带来对性能的影响。

解决以上问题可以使用以下几种方式

  1. 可以将DOM写成html字符串,然后使用innerHTML添加到页面中,但这种操作会比较麻烦,且不方便使用节点操作的相关方法。
  2. 使用createDocumentFragment来管理节点时,此时节点都在内存中,而不是DOM树中。对节点的操作不会引发页面回流,带来比较好的性能体验。

DocumentFragment特点

  • createDocumentFragment父节点为null
  • 继承自node所以可以使用NODE的属性和方法
  • createDocumentFragment创建的是文档碎片,节点类型nodeType为11。因为不在DOM树中所以只能通过JS进行操作
  • 添加createDocumentFragment添加到DOM后,就不可以再操作createDocumentFragment元素了,这与DOM操作是不同的
  • 将文档DOM添加到createDocumentFragment时,会移除文档中的DOM元素
  • createDocumentFragment创建的节点添加到其他节点上时,会将子节点一并添加
  • createDocumentFragment是虚拟节点对象,不直接操作DOM所以性能更好
  • 在排序/移动等大量DOM操作时建议使用createDocumentFragment

表单控制

表单是高频操作的元素

表单查找

JS为表单的操作提供了单独的集合控制

  • 使用document.forms获取表单集合
  • 使用form的name属性获取指定form元素
  • 根据表单项的name属性使用form.elements.title获取表单项,
  • 也可以直接写成form.name形式,不需要form.elements.title
  • 针对radio/checkbox获取的表单项是一个集合
<form action="" name="mk">
    <input type="text" name="title" />
</form>
<script>
    const form = document.forms.mk
    console.log(form.elements.title)
</script>

通过表单项可以反向查找FORM

<form action="" name="mk">
    <input type="text" name="title" />
</form>
<script>
    const form = document.forms.mk
    console.log(form.elements.form === form)
</script>

样式管理

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

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

批量设置

app.className = 'mkimq'

app.setAttribute('class', 'mkimq')

classList

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

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

设置行样式

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

样式属性设置

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

  • 多个单词的属性使用驼峰进行命名
app.style.backgroundColor = 'red'
app.style.color = 'white'

批量设置行样式

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

app.style.cssText = 'background-color: red;color: white;'

setAttribute改变style特征来批量设置样式

app.setAttribute('style', 'background-color: red;color: white;')

获取样式

可以通过style对象,window.window.getComputedStyle对象获取样式属性,

style

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

  • style对象不能获取行样式外定义的样式
app.style.backgroundColor
app.style.color

getComputedStyle

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

  • 函数第一个参数为元素
  • 第二个参数为伪类
  • 这是计算后的样式属性,所以取得的单位和定义时的可能会有不同
const fontSize = window.getComputedStyle(app).fontSize
贡献者: mankueng