发现问题:
今天在写一个特别简单的jQuery代码的时候,发现了一个bug,当同时使用append和perpend时,代码中靠后的会将靠前的覆盖掉,具体如下:(jQuery版本为3.6.4)
<body>
<ul>
<li class="old">old</li>
</ul>
<script>
$(function () {
const New = $("<li>new</li>")
$("ul").append(New) // 向子代最后插入
$("ul").prepend(New) // 向子代最前插入
})
</script>
</body>
运行结果为
后面经验证,单独使用时没有任何问题,但是同时使用就是会出现问题,即后面的会覆盖掉前面的
<body>
<ul>
<li class="old">old</li>
</ul>
<script>
$(function () {
const New = $("<li>new</li>")
//反转一下代码顺序
$("ul").prepend(New) // 向子代最前插入
$("ul").append(New) // 向子代最后插入
})
</script>
</body>
结果如下:
那么到底是什么问题导致的呢,去翻了翻jQuery源文件发现,append和prepend是用如下代码实现的:
append: function() {
return domManip( this, arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.appendChild( elem );
}
} );
},
prepend: function() {
return domManip( this, arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.insertBefore( elem, target.firstChild );
}
} );
},
看了看,感觉也没有什么会引起冲突的地方,都是一些基础节点操作,无从下手怎么办,删代码!对jQuery源代码进行删删改改发现,不管怎么改都会冲突。最后突然想到,有没有可能是appendChild和insertBefore这两个源代码就冲突呢?说干就干:
<body>
<ul>
<li class="old">old</li>
</ul>
<script>
const New = document.createElement('li')
New.innerText = "new"
const ul = document.querySelector('ul')
const old = document.querySelector('.old')
ul.appendChild(New)//向子代最后插入
ul.insertBefore(New, old)//向子代中的old前插入,相当于向子代最前插入
</script>
</body>
运行结果:
哈哈,不出所料,问题出在根源上了,原生js的问题,那...到底是什么导致的这个原生js问题呢?
解决问题:
跟据mdn官方文档可知:
Node.appendChild()
方法将一个节点附加到指定父节点的子节点列表的末尾处。如果将被插入的节点已经存在于当前文档的文档树中,那么appendChild()
只会将它从原先的位置移动到新的位置(不需要事先移除要移动的节点)。
也就是说,这个新创建的节点,由于保存到了New中,地址相同,在同一文档中就会被当作是同一节点,JS规定:同一节点不可能被插入到文档的不同位置。
当然了,官方也给出了相应的解决办法:
若要保留已在文档中的节点,可以先使用 Node.cloneNode() 方法来为它创建一个副本,再将副本附加到目标父节点下。请注意,用
cloneNode
制作的副本不会自动保持同步。
<body>
<ul>
<li class="old">old</li>
</ul>
<script>
const New1 = document.createElement('li')
New1.innerText = "new"
const New2 = New1.cloneNode()
New2.innerText = "new" // cloneNode()只会复制节点,不会复制节点内的内容,需重新给节点赋值
const ul = document.querySelector('ul')
const old = document.querySelector('.old')
ul.insertBefore(New1, old)//向子代中的old前插入,相当于向子代最前插入
ul.append(New2)//向子代最后插入
</script>
</body>
<ul>
<li class="old">old</li>
</ul>
<script>
$(function () {
const New1 = $("<li>new</li>")
const New2 = New1.clone()
$("ul").prepend(New1) // 向子代最前插入
$("ul").append(New2) // 向子代最后插入
})
</script>
这样就解决了这个问题:
运行结果如下:
当然,除了使用clone()方法外,还有一种方法:
<body>
<ul>
<li class="old">old</li>
</ul>
<script>
$(function () {
$("ul").prepend($("<li>new</li>")) // 向子代最前插入
$("ul").append($("<li>new</li>")) // 向子代最后插入
})
</script>
</body>
照样可以实现这个功能
同理,也可以解决jQuery中before和after冲突问题
至此,这个问题已经解决完成
有什么问题可以评论区讨论
下次见啦~