Wrap Elements with same class next to each other in plain Javascript

Viewed 255

Looking for a way to wrap elements next to each other with same class in a wrapper. This can easily be done in jquery but I'm struggling to find a way in pure JS. I was able to remove empty tags between .group but not sure how to wrap those elements.

http://jsfiddle.net/chille1987/6cnjgft0/4/

Current HTML

<div class="group">1</div>
<div class="group">2</div>
<p><br /></p>
<div class="group">3</div>

<p>Lorem ipsum dolor sit amet</p>

<section>
    <div class="group">4</div>
    <div class="group">5</div>
</section>

<p>Lorem ipsum dolor sit amet</p>

<div class="group">6</div>

Expected HTML

<div class="wrapper">
    <div class="group">1</div>
    <div class="group">2</div> 
    <div class="group">3</div>
</div>

<p>Lorem ipsum dolor sit amet</p>

<section>
    <div class="wrapper">
        <div class="group">4</div>
        <div class="group">5</div>
    </div>
</section>    

<p>Lorem ipsum dolor sit amet</p>

<div class="wrapper">
    <div class="group">6</div>
</div>

Javascipt

// Create Wrapper
let wrapper = document.createElement('div');
wrapper.classList.add('wrapper');

// Add class to br parent
let brTags = document.querySelectorAll('br:first-child');
brTags.forEach(empty => {
    empty.parentNode.classList.add('empty-paragraph');
});

// remove empty paragraphs between groups
const empty = document.querySelectorAll('.empty-paragraph');
empty.forEach(paragraph => {
    if (paragraph.nextElementSibling.classList.contains('group') && paragraph.previousElementSibling.classList.contains('group')) {
        paragraph.remove();
    }
});
1 Answers

I figure I'd do it parent-by-parent, and I'd remove "blank" paragraphs in one pass then group things in another (you could do it all in one pass, but it's more complicated).

See comments inline:

// A function to handle one parent
function groupChildren(parent) {
    let child;

    // Remove "blank" paragraphs first
    child = parent.firstElementChild;
    while (child) {
        // Grab this first, since we may remove `child`
        const next = child.nextElementSibling;
        if (!child.classList.contains("group")) {
            // "Blank" means it has nothing but BR elements or blank text nodes
            const remove = child.tagName === "P" && Array.from(child.childNodes).every(c =>
                (c.nodeType === Node.TEXT_NODE && !c.nodeValue) ||
                (c.tagName === "BR")
            );
            if (remove) {
                parent.removeChild(child);
            }
        }
        child = next;
    }

    // Find and group .group paragraphs
    let group;
    child = parent.firstElementChild;
    while (child) {
        const next = child.nextElementSibling;
        if (child.classList.contains("group")) {
            if (!group) {
                // No group yet, create one and insert it
                group = document.createElement("div");
                group.classList.add("wrapper");
                parent.insertBefore(group, child);
            }
            // Move this into the current group
            group.appendChild(child);
        } else {
            // Break the group here
            group = null;
        }
        child = next;
    }
}

// Handle all of the `.group` elements; often later ones will have been
// handled when handling earlier ones in the same parent, so check for that
for (const child of Array.from(document.getElementsByClassName("group"))) {
    if (!child.parentElement.classList.contains("wrapper")) {
        groupChildren(child.parentElement);
    }
}
<div class="group">1</div>
<div class="group">2</div>
<p><br /></p>
<div class="group">3</div>

<p>Lorem ipsum dolor sit amet</p>

<section>
    <div class="group">4</div>
    <div class="group">5</div>
</section>

<p>Lorem ipsum dolor sit amet</p>

<div class="group">6</div>

Related