如何在 JavaScript 中只删除父元素而不删除其子元素?

比如说:

<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>

回到这里:

<div>
pre text
<p>child foo</p>
<p>child bar</p>
nested text
post text
</div>

我一直在研究如何使用 Mootools、 jQuery 甚至(原始的) JavaScript,但不知道如何做到这一点。

71431 次浏览

Using jQuery you can do this:

var cnt = $(".remove-just-this").contents();
$(".remove-just-this").replaceWith(cnt);

Quick links to the documentation:

Whichever library you are using you have to clone the inner div before removing the outer div from the DOM. Then you have to add the cloned inner div to the place in the DOM where the outer div was. So the steps are:

  1. Save a reference to the outer div's parent in a variable
  2. Copy the inner div to another variable. This can be done in a quick and dirty way by saving the innerHTML of the inner div to a variable or you can copy the inner tree recursively node by node.
  3. Call removeChild on the outer div's parent with the outer div as the argument.
  4. Insert the copied inner content to the outer div's parent in the correct position.

Some libraries will do some or all of this for you but something like the above will be going on under the hood.

The library-independent method is to insert all child nodes of the element to be removed before itself (which implicitly removes them from their old position), before you remove it:

while (nodeToBeRemoved.firstChild)
{
nodeToBeRemoved.parentNode.insertBefore(nodeToBeRemoved.firstChild,
nodeToBeRemoved);
}


nodeToBeRemoved.parentNode.removeChild(nodeToBeRemoved);

This will move all child nodes to the correct place in the right order.

You should make sure to do this with the DOM, not innerHTML (and if using the jQuery solution provided by jk, make sure that it moves the DOM nodes rather than using innerHTML internally), in order to preserve things like event handlers.

My answer is a lot like insin's, but will perform better for large structures (appending each node separately can be taxing on redraws where CSS has to be reapplied for each appendChild; with a DocumentFragment, this only occurs once as it is not made visible until after its children are all appended and it is added to the document).

var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);

And, since you tried in mootools as well, here's the solution in mootools.

var children = $('remove-just-this').getChildren();
children.replaces($('remove-just-this');

Note that's totally untested, but I have worked with mootools before and it should work.

http://mootools.net/docs/Element/Element#Element:getChildren

http://mootools.net/docs/Element/Element#Element:replaces

 $('.remove-just-this > *').unwrap()

if you'd like to do this same thing in pyjamas, here's how it's done. it works great (thank you to eyelidness). i've been able to make a proper rich text editor which properly does styles without messing up, thanks to this.

def remove_node(doc, element):
""" removes a specific node, adding its children in its place
"""
fragment = doc.createDocumentFragment()
while element.firstChild:
fragment.appendChild(element.firstChild)


parent = element.parentNode
parent.insertBefore(fragment, element)
parent.removeChild(element)

More elegant way is

$('.remove-just-this').contents().unwrap();

I was looking for the best answer performance-wise while working on an important DOM.

eyelidlessness's answer was pointing out that using javascript the performances would be best.

I've made the following execution time tests on 5,000 lines and 400,000 characters with a complexe DOM composition inside the section to remove. I'm using an ID instead of a class for convenient reason when using javascript.

Using $.unwrap()

$('#remove-just-this').contents().unwrap();

201.237ms

Using $.replaceWith()

var cnt = $("#remove-just-this").contents();
$("#remove-just-this").replaceWith(cnt);

156.983ms

Using DocumentFragment in javascript

var element = document.getElementById('remove-just-this');
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);

147.211ms

Conclusion

Performance-wise, even on a relatively big DOM structure, the difference between using jQuery and javascript is not huge. Surprisingly $.unwrap() is most costly than $.replaceWith(). The tests have been done with jQuery 1.12.4.

If you are dealing with multiple rows, as it was in my use case you are probably better off with something along these lines:

 $(".card_row").each(function(){
var cnt = $(this).contents();
$(this).replaceWith(cnt);
});

Use modern JS!

const node = document.getElementsByClassName('.remove-just-this')[0];
node.replaceWith(...node.childNodes); // or node.children, if you don't want textNodes

oldNode.replaceWith(newNode) is valid ES5

...array is the spread operator, passing each array element as a parameter

Replace div with its contents:

const wrapper = document.querySelector('.remove-just-this');
wrapper.outerHTML = wrapper.innerHTML;
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>

The solution with replaceWith only works when there is one matching element. When there are more matching elements use this:

$(".remove-just-this").contents().unwrap();