DOM节点增删查改替换复制
这篇文章中总结的节点操作比较狭义,特指对 element 节点的操作,并没有包含其他内容,至于对文本内容和属性的操作就放在下一篇吧~
节点的“查”操作已经在上一篇文章中总结过了,在本文中主要包括创建节点、修改节点、插入节点、删除节点、替换节点、查看节点和复制节点
今天的通过下面这一段范例代码展开:
<ul id="users">
<li class="user">小A君</li>
<li class="user">小C君</li>
</ul>
我们可以设想这样一个应用场景,根据上面的结构,创建两个新的“小B君”和“小D君”,按照 ABCD 的顺序插入到上面的列表中
那么我们就开始吧~
创建节点
createElement
创建 element 节点主要依赖 createElement
方法,这个方法接受一个参数,即要创建的标签名:
let tempLi = document.createElement('li');
console.log(tempLi); // <li></li>
这个标签名并不区分大小写,但是按照标准最好还是采用小写~
当 element 被创建的时候,它所属的 document 就被设置好了,
修改节点
当一个空的 element 创建出来后,就要根据我们的需求为其添加内容
如果我们要添加的是文本内容,可以采用下面的几个方法;
- innerText
- textContent
而关于添加(插入)节点的内容就放在下一节 插入节点 中
innerText
innerText 属性是一个可读可写的属性,它可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过innerText读取值时,它会按照由浅入深地顺序,将子文档树中的所有文本拼接起来。在通过innerText写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点
console.log(document.getElementById('users').innerText); // 小A君 小B君
而当我们进行写操作的时候,会直接将这个 element 中的所有子节点清除并添加一个文本节点(可以采用 element.innerText = ''
来清空一个元素),猜一下当我们执行下面这段代码的时候会发生什么:
let usersEle = document.getElementById('users');
usersEle.innerText = usersEle.innerText;
(如果真的写了这样的代码会被人打死的吧,咳咳~)
textContent
这个属性和 innerText 的使用方法基本一致,textContent 属性与 innerText 属性类似,该属性可读写。在读模式下,返回当前节点和它的所有后代节点的文本内容;在写模式下,结果会删除元素的所有子节点,插入包含相应文本值的文本节点
他们的主要区别在于 innerText 是 element 的属性,而 textContent 是 node 的属性且 textContent 不兼容 IE9 以下浏览器
console.log(document.getElementById('users').textContent); // 小A君 小B君
插入节点
在创建好节点并添加好内容之后我们应该将节点插入到他们的父节点中了,插入节点的方法也不止一种
- appendChild
- insertBefore
appendChild
这个方法的作用可以很明显通过方法名理解,就是直接将一个 child 节点 append 进来,在节点的最后追加一个子节点
使用方法:
let tempLi = document.createElement('li');
tempLi.innerText = "小D君";
document.getElementById('users').appendChild(tempLi);
由于这个方法只能将节点添加到父节点的最后,所以在使用时需要注意顺序方面的要求,通常的做法是先进行一定的排序后再调用这个方法
⚠️注意: 如果要添加的节点已经存在在这个 document 中了,并不是新创建出来的 element,这时会发生的并不是单纯的插入,而是“移动”
<!-- html body -->
<ul id="users">
<li class="user">小A君</li>
<li class="user">小C君</li>
</ul>
<script>
document.getElementById('users').appendChild(
document.getElementsByClassName('user')[0]
)
</script>
insertBefore
insertBefore() 方法接收两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个兄弟节点(previousSibling),同时被方法返回。如果参照节点是null,则insertBefore()与appendChild()方法执行相同的操作
同样地,如果插入的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置
这个方法就更加适合 “小B君” 的插入
let tempLi = document.createElement('li');
tempLi.innerText = "小B君";
document.getElementById('users').insertBefore(
tempLi,
document.getElementsByClassName('user')[1]
);
删除节点
removeChild
removeChild 方法接收一个参数,即要移除的节点,被移除的节点成为方法的返回值
let user0 = document.getElementsByClassName('user')[0];
document.getElementById('users').removeChild(user0);
remove()
remove 方法不太常见,直接在当前节点使用 remove() 方法就可以删除该节点,无返回值
要实现和上面一样的效果只需要
document.getElementsByClassName('user')[0].remove();
替换节点
replaceChild
replaceChild 接收的两个参数是要插入的节点和要替换的节点,要替换的节点将由这个方法返回并从文档树中移除,同时由要插入的节点占据其位置
let tempLi = document.createElement('li');
tempLi.innerText = "替换节点";
document.getElementById('users').replaceChild(
tempLi,
document.getElementsByClassName('user')[0]
);
复制节点
cloneNode
cloneNode 方法用于克隆一个节点
调用者是要被复制的 node,返回值是这个 node 的副本
它接受一个布尔值作为参数,表示是否执行深复制。在参数为true时,执行深复制,也就是复制节点及整个子节点树。在参数为false的情况下,执行浅复制,即复制节点本身
let dupUsers = document.getElementById('users').cloneNode(true);
console.log(dupUsers);
⚠️注意: 由于不同版本规范中 cloneNode 方法的默认参数不同,所以请一定加上参数
终极大招 innerHTML
上述提到的所有问题,都可以通过 innerHTML 解决
这个方法和前文中提到的 innerText 很相似
- 在读模式下,返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的HTML字符串;
- 在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点
我们可以在读模式下获取到我们要操作的 node 或者他的 父节点,之后通过字符串处理方法进行操作,然后再写入
但是我们一般不会这样做
主要是因为它存在安全问题,innerHTML不会检查代码,直接运行,会有风险
所以innerHTML方法建议仅用于新的节点,比如创建插入,内容最好是可控的,而不是用户填写的内容