Home > Software design >  How do I wrap adjacent elements of the same class using Javascript (no jQuery)
How do I wrap adjacent elements of the same class using Javascript (no jQuery)

Time:01-19

Everywhere I looked, it seemed that this problem has only been solved using jQuery, which I'm trying to remove completely from my project. Here's the HTML:

<div ></div>
<div ></div>
<div ></div>
<p></p>
<div ></div>
<div ></div>
<p></p>
<div ></div>

desired result:

<div >
   <div ></div>
   <div ></div>
   <div ></div>
</div>
<p></p>
<div >
   <div ></div>
   <div ></div>
</div>
<p></p>
<div >
   <div ></div>
</div>

And here's how this can be done using jQuery, thanks to the many answers I've found on the topic

const e = '.codeblock';
$(e).not(e   ' '   e).each(function () {
$(this).nextUntil(':not('   e   ')').addBack().wrapAll('<div  />');
});

Is there a way to replicate this same functionality using vanilla Javascript? I've tried using Element.nextElementSibling and checking if the class matches, but this approach wasn't very elegant and resulted in much more code than the jQuery solution.

CodePudding user response:

You could do something like this:

// Find all elements that match the class
document.querySelectorAll(`.${e}`).forEach(
    // For each elemnt
    elem => {
        // If it's not the first of the group, skip it
        if (elem.previousElementSibling!==null && elem.previousElementSibling.classList.contains(e)){
            return;
        }

        // Find all adjacent elements with the same class
        let o = [elem];
        while (o[o.length - 1].nextElementSibling.classList.contains(e)) {
             o.push(o[o.length - 1].nextElementSibling);
        }

        // Create a new wrapper element and give it a proper class
        let wrapper = document.createElement('div');
        wrapper.classList.add('contentBox');

        // Insert the new wrapper immediatly before the group
        elem.insertAdjacentElement('beforebegin', wrapper);

        // Move the contents of the group to inside the wrapper element
        wrapper.replaceChildren(...o);
    }
)

CodePudding user response:

It's a bit more code, but you can loop through all div and p, check every element and when matched append it to a new or existing div.codeBlock.

const isTargeted = el => el.classList.contains(`codeblock`);
const createWrap = (beforeEl) => beforeEl.insertAdjacentElement(`beforebegin`, 
  Object.assign(document.createElement(`div`), {className: `contentBox`}));
const divsAndPs = document.querySelectorAll(`div, p`);
divsAndPs.forEach(
  (elem, i, self) => {
    if (!i || isTargeted(elem)) {
      const wrap = i && self[i-1].closest(`.contentBox`) || 
        createWrap(elem);
      wrap.appendChild(elem);
    } 
  }
);
.contentBox {
  color: green;
}

.contentBox .codeblock {
  margin-left: 2rem;
}

.contentBox:before {
  content: 'I am the great contentBox, here are my codeblocks:';
  color: grey;
}
<div >x</div>
<div >x</div>
<div >x</div>
<p>paragraph</p>
<div >x</div>
<div >x</div>
<p>paragraph</p>
<div >x</div>

  • Related