¿Cómo mover todos los elementos secundarios HTML a otro padre usando JavaScript?

80

Imagina:

<div id="old-parent">
    <span>Foo</span>
    <b>Bar</b>
    Hello World
</div>
<div id="new-parent"></div>

¿Qué JavaScript se puede escribir para mover todos los nodos secundarios (tanto elementos como nodos de texto) de old-parenta new-parentsin jQuery?

No me importa el espacio en blanco entre los nodos, aunque espero atrapar a los extraviados Hello World, también tendrían que migrarlos como están.

EDITAR

Para ser claro, quiero terminar con:

<div id="old-parent"></div>
<div id="new-parent">
    <span>Foo</span>
    <b>Bar</b>
    Hello World
</div>

La respuesta a la pregunta de la que se trata de un duplicado propuesto resultaría en:

<div id="new-parent">
    <div id="old-parent">
        <span>Foo</span>
        <b>Bar</b>
        Hello World
    </div>
</div>
Dibujó Noakes
fuente
6
@MarcB, esto no es un duplicado. La pregunta a la que enlaza no trata de mover a todos los niños, ni ninguna respuesta a esa pregunta aborda mi necesidad de mover diferentes tipos de nodos HTML (como nodos de texto).
Drew Noakes
2
la respuesta es exactamente lo que necesita. dom es un árbol. mueve un nodo, todos los hijos de ese nodo se mueven con él.
Marc B
@MarcB a veces no desea que el objeto principal en el destino. Considere, por ejemplo, mover todos los elementos de una lista TODO de los elementos principales 'pendientes' a 'completos'.
Drew Noakes

Respuestas:

124

MANIFESTACIÓN

Básicamente, desea recorrer cada descendiente directo del nodo padre antiguo y moverlo al nuevo padre. Cualquier hijo de un descendiente directo se moverá con él.

var newParent = document.getElementById('new-parent');
var oldParent = document.getElementById('old-parent');

while (oldParent.childNodes.length > 0) {
    newParent.appendChild(oldParent.childNodes[0]);
}
Persona especial
fuente
8
Para ser realmente eficiente: while (oldParent.childNodes.length) { newParent.appendChild(oldParent.firstChild); }
Gibolt
4
Tenga en cuenta que childNodes [] normalmente también contiene nodos de texto vacíos (uno por cada salto de línea en el código fuente).
Dercsár
5
@Gibolt Creé una prueba JSPerf y parece indicarme que el acceso a la matriz es más rápido que llamar firstChild. La diferencia es insignificante en el mejor de los casos. Probablemente también variará de un navegador a otro. Sin firstChildembargo, creo que es más legible.
aplastar
17
while (oldParent.hasChildNodes()) newParent.appendChild(oldParent.firstChild);y while (oldParent.firstChild) newParent.appendChild(oldParent.firstChild);trabaja 2 veces más rápido en Chromium y de 6 a 10 veces más rápido en Firefox. Su JSPerf modificado
usuario
2
Puede ser beneficioso utilizar DocumentFragment developer.mozilla.org/en-US/docs/Web/API/DocumentFragment y mover todos los elementos a la vez si lo que busca es rendimiento.
Íhor Mé
9

Manera moderna:

newParent.append(...oldParent.childNodes);
  1. .appendes el reemplazo de .appendChild. La principal diferencia es que acepta varios nodos a la vez e incluso cadenas simples, como.append('hello!')
  2. oldParent.childNodeses iterable, por lo que puede extenderse ...para convertirse en múltiples parámetros de.append()

Tablas de compatibilidad de ambos (en resumen: Edge 17+, Safari 10+):

fregante
fuente
Parece muy elegante. Me pregunto si hay una diferencia de rendimiento con las otras respuestas aquí.
Drew Noakes
Si lo hay, debe ser mínimo. En todo caso, es más rápido porque es una sola append llamada en lugar de muchas.
fregante
Tenga cuidado: no para IE11 (nunca, probablemente)
Rene van der Lende
Bien, pero hice algunas pruebas simples y while()todavía es un poco más rápido en Chrome. No tome mi palabra y realice sus propias pruebas si es posible.
lepe
8

Aquí tienes una función simple:

function setParent(el, newParent)
{
    newParent.appendChild(el);
}

el's childNodesson los elementos que se moverán, newParent es el elemento al elque se moverá, por lo que ejecutaría la función como:

var l = document.getElementById('old-parent').childNodes.length;
var a = document.getElementById('old-parent');
var b = document.getElementById('new-parent');
for (var i = l; i >= 0; i--)
{
    setParent(a.childNodes[0], b);
}

Aquí está la demostración

Cilan
fuente
¿Cómo usarías esto con su código? Esto también movería el old-parentelemento si lo pasara como el.
aplastar el
1
Eso representaría HTML:<div id="new-parent"><div id="old-parent">...</div></div>
aplastar el
1
Eso tampoco funcionará porque a medida que mueve al niño, la longitud cambia. Entonces, terminarás moviendo solo la mitad de los nodos. Necesita iterar hacia atrás o usar un ciclo while como lo hice yo.
aplastar el
Todavía no funcionará. Necesita hacer: for (var i = l; i >= 0; i--)o while (document.getElementById('old-parent').length > 0). Además, su demostración tiene errores.
aplastar el
@crush mira editar, sry -while (document.getElementById('old-parent').length > 0) { setParent(document.getElementById('old-parent')[i], document.getElementById('new-parent')); }
Cilan
0

Esta respuesta solo funciona realmente si no necesita hacer nada más que transferir el código interno (innerHTML) de uno a otro:

// Define old parent
var oldParent = document.getElementById('old-parent');

// Define new parent
var newParent = document.getElementById('new-parent');

// Basically takes the inner code of the old, and places it into the new one
newParent.innerHTML = oldParent.innerHTML;

// Delete / Clear the innerHTML / code of the old Parent
oldParent.innerHTML = '';

¡Espero que esto ayude!

ItsJonQ
fuente
1
Esto funciona, aunque realmente no quiero serializar / deserializar a través de cadenas HTML. Aunque funciona :)
Drew Noakes
11
No copiará ninguno de los objetos y eventos adjuntos a los elementos
Gregory Magarshak
4
Este DOM regenerado, no mueve nodos / eventos existentes y probablemente es mucho más lento que un bucle en todos los niños
Jordan
Ejecuté algunas pruebas simples y este método aún es más lento que la respuesta aceptada, y tiene desventajas como ya se explicó.
lepe
-1

Si no lo usa -en los nombres de identificación, puede hacer esto

oldParent.id='xxx';
newParent.id='oldParent';
xxx.id='newParent';
oldParent.parentNode.insertBefore(oldParent,newParent);
#newParent { color: red }
<div id="oldParent">
    <span>Foo</span>
    <b>Bar</b>
    Hello World
</div>
<div id="newParent"></div>

Kamil Kiełczewski
fuente