How to Unwrap an Element?

Viewed 630

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);
4 Answers

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>

Try this - vanilla JS. jQuery has an unwrap() but this is simpler

Note innerHTML will lose the event handlers

document.querySelector(".parent").innerHTML = [...document.querySelectorAll(".child")]
  .map(div => div.innerHTML).join("")
<div class="parent">
    <div class="child">
        <div class="grandchild">g1</div>
        <div class="grandchild">g2</div>
        <div class="grandchild">g3</div>
    </div>
    <div class="child">
        <div class="grandchild">g4</div>
        <div class="grandchild">g5</div>
        <div class="grandchild">g6</div>
    </div>
</div>

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>

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>

Related