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>