6

Is there a way of "opening up a div" using JavaScript (remove a parent but keep intact its child nodes)?

What I mean by this is changing this:

<div class="parent">
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
</div>

To this:

<div class="parent">
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
    <div class="grandchild"></div>
</div>

Unfortunately I don't have access to the HTML so I'm thinking I could achieve this result using JavaScript after the webpage has been loaded.

So far I have:

const children = document.querySelectorAll('.child');
children.forEach(child => //not sure what to do next);
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
AgentH
  • 91
  • 6
  • 1
    You mean `unwrap` – mplungjan Jul 29 '21 at 10:14
  • 1
    Basically you need to remove each "grandchild" element from the DOM and then re-insert it under the "parent" element instead. And remove all the "child" ones completely. https://developer.mozilla.org/en-US/docs/Web/API/Element/remove and https://developer.mozilla.org/en-US/docs/Web/API/Element/append are likely to be helpful to you – ADyson Jul 29 '21 at 10:14
  • Yeah sorry I know that is quite vague. What I mean is essentially deleting a div without deleting the content inside of it – AgentH Jul 29 '21 at 10:16
  • 1
    `What I mean is essentially deleting a div without deleting the content inside of it`...for that you need to first move the content elsewhere, as I've alluded to above. – ADyson Jul 29 '21 at 10:16
  • @ADyson Thank you, I'll have to do it in this way then. Wasn't sure if there was a function that could do this for me. Thanks a lot. – AgentH Jul 29 '21 at 10:20
  • @AgentH well the answers below are showing you ways to achieve it quite neatly with one-liners combining a few functions, so near-enough having a ready-made function – ADyson Jul 29 '21 at 10:20

3 Answers3

9

Remove an element but keep its child nodes is commonly known as "unwrap".
There's a pretty handy Element.replaceWith() method:

document.querySelectorAll(".child")
  .forEach(EL => EL.replaceWith(...EL.childNodes));
.child { padding: 5px; background: gold; }
.grandchild { padding: 10px; background:#eee; margin: 5px 0;}
<div class="parent">
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
    <div class="child">
        <div class="grandchild"></div>
        <div class="grandchild"></div>
        <div class="grandchild"></div>
    </div>
</div>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • A bit heavy on the DOM manipulation? – mplungjan Jul 29 '21 at 10:20
  • 1
    @mplungjan I would not say. You should not use `innerHTML` for replacements anyways. Just one case of many is: if your elements have already assigned events, data, etc, you're losing it all. – Roko C. Buljan Jul 29 '21 at 10:22
2

You can move all the .grandchildren to the .parent:

grandchildren.forEach((grandchild) => parent.appendChild(grandchild));

And then you can remove all the .children:

children.forEach((child) => parent.removeChild(child));

Working Example:

const parent = document.querySelector('.parent');
const children = document.querySelectorAll('.child');
const grandchildren = document.querySelectorAll('.grandchild');

grandchildren.forEach((grandchild) => parent.appendChild(grandchild));
children.forEach((child) => parent.removeChild(child));
<div class="parent">
    <div class="child">
        <div class="grandchild">1</div>
        <div class="grandchild">2</div>
        <div class="grandchild">3</div>
    </div>
    <div class="child">
        <div class="grandchild">4</div>
        <div class="grandchild">5</div>
        <div class="grandchild">6</div>
    </div>
    <div class="child">
        <div class="grandchild">7</div>
        <div class="grandchild">8</div>
        <div class="grandchild">9</div>
    </div>
    <div class="child">
        <div class="grandchild">10</div>
        <div class="grandchild">11</div>
        <div class="grandchild">12</div>
    </div>
</div>
Rounin
  • 27,134
  • 9
  • 83
  • 108
-1

If using jQuery is an option, it has a unwrap() method for that. I'm not sure how easy that is with pure JS though.

$(".grandchild").unwrap()
.parent {
   border : green solid 2px;
}

.child {
   border : red solid 2px;
}
.grandchild {
   border : yellow solid 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
    <div class="child">
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
    </div>
    <div class="child">
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
    </div>
    <div class="child">
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
    </div>
    <div class="child">
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
        <div class="grandchild">123456</div>
    </div>
</div>
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63