Обтекание набора элементов DOM с использованием JavaScript
У меня есть серия тегов p
на моей странице, и я хочу их обернуть в контейнер, например.
<p>foo</p>
<p>bar</p>
<p>baz</p>
Я хочу обернуть все вышеуказанные теги в контейнер следующим образом:
<div>
<p>foo</p>
<p>bar</p>
<p>baz</p>
</div>
Как обернуть NodeList
в элемент с использованием ванильного JavaScript?
Ответы
Ответ 1
Вы можете сделать следующее:
// create the container div
var dv = document.createElement('div');
// get all divs
var divs = document.getElementsByTagName('div');
// get the body element
var body = document.getElementsByTagName('body')[0];
// apply class to container div
dv.setAttribute('class', 'container');
// find out all those divs having class C
for(var i = 0; i < divs.length; i++)
{
if (divs[i].getAttribute('class') === 'C')
{
// put the divs having class C inside container div
dv.appendChild(divs[i]);
}
}
// finally append the container div to body
body.appendChild(dv);
Ответ 2
Ниже представлена чистая версия JavaScript методов jQuery wrap
и wrapAll
. Я не могу гарантировать, что они работают точно так же, как в jQuery, но они действительно работают очень похожими и должны быть способны выполнять одни и те же задачи. Они работают либо с одним HTMLElement, либо с массивом из них. Я не тестировал, чтобы подтвердить, но они должны работать во всех современных браузерах (и в более ранних версиях).
В отличие от выбранного ответа эти методы поддерживают правильную структуру HTML, используя insertBefore, а также appendChild.
компресс:
// Wrap an HTMLElement around each element in an HTMLElement array.
HTMLElement.prototype.wrap = function(elms) {
// Convert `elms` to an array, if necessary.
if (!elms.length) elms = [elms];
// Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
for (var i = elms.length - 1; i >= 0; i--) {
var child = (i > 0) ? this.cloneNode(true) : this;
var el = elms[i];
// Cache the current parent and sibling.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the element (is automatically removed from its current
// parent).
child.appendChild(el);
// If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
// append it to the parent.
if (sibling) {
parent.insertBefore(child, sibling);
} else {
parent.appendChild(child);
}
}
};
См. рабочую демонстрацию в jsFiddle.
wrapAll:
// Wrap an HTMLElement around another HTMLElement or an array of them.
HTMLElement.prototype.wrapAll = function(elms) {
var el = elms.length ? elms[0] : elms;
// Cache the current parent and sibling of the first element.
var parent = el.parentNode;
var sibling = el.nextSibling;
// Wrap the first element (is automatically removed from its
// current parent).
this.appendChild(el);
// Wrap all other elements (if applicable). Each element is
// automatically removed from its current parent and from the elms
// array.
while (elms.length) {
this.appendChild(elms[0]);
}
// If the first element had a sibling, insert the wrapper before the
// sibling to maintain the HTML structure; otherwise, just append it
// to the parent.
if (sibling) {
parent.insertBefore(this, sibling);
} else {
parent.appendChild(this);
}
};
См. рабочую демонстрацию на jsFiddle.
Ответ 3
Здесь моя javascript версия wrap(). Короче, но вы должны создать элемент перед вызовом функции.
HTMLElement.prototype.wrap = function(wrapper){
this.parentNode.insertBefore(wrapper, this);
wrapper.appendChild(this);
}
function wrapDiv(){
var wrapper = document.createElement('div'); // create the wrapper
wrapper.style.background = "#0cf"; // add style if you want
var element = document.getElementById('elementID'); // get element to wrap
element.wrap(wrapper);
}
div {
border: 2px solid #f00;
margin-bottom: 10px;
}
<ul id="elementID">
<li>Chair</li>
<li>Sofa</li>
</ul>
<button onclick="wrapDiv()">Wrap the list</button>
Ответ 4
Если вы поддерживаете целевые браузеры, document.querySelectorAll
использует селектор CSS:
var targets = document.querySelectorAll('.c'),
head = document.querySelectorAll('body')[0],
cont = document.createElement('div');
cont.className = "container";
for (var x=0, y=targets.length; x<y; x++){
con.appendChild(targets[x]);
}
head.appendChild(cont);
Ответ 5
Я пришел к этой функции wrapAll
, начав с ответа Кевина и установив проблемы, представленные ниже, а также те, которые указаны в комментариях ниже его ответа.
- Его функция пытается добавить оболочку к следующему брату первого node в переданном узлеList. Это будет проблематично, если этот node также находится в nodeList. Чтобы увидеть это в действии, удалите весь текст и другие элементы между первым и вторым
<li>
в его демоверсию wrapAll.
- В отличие от претензии, его функция не будет работать, если несколько узлов передаются в массиве, а не в nodeList из-за используемого метода петли.
Они указаны ниже:
// Wrap wrapper around nodes
// Just pass a collection of nodes, and a wrapper element
function wrapAll(nodes, wrapper) {
// Cache the current parent and previous sibling of the first node.
var parent = nodes[0].parentNode;
var previousSibling = nodes[0].previousSibling;
// Place each node in wrapper.
// - If nodes is an array, we must increment the index we grab from
// after each loop.
// - If nodes is a NodeList, each node is automatically removed from
// the NodeList when it is removed from its parent with appendChild.
for (var i = 0; nodes.length - i; wrapper.firstChild === nodes[0] && i++) {
wrapper.appendChild(nodes[i]);
}
// Place the wrapper just after the cached previousSibling,
// or if that is null, just before the first child.
var nextSibling = previousSibling ? previousSibling.nextSibling : parent.firstChild;
parent.insertBefore(wrapper, nextSibling);
return wrapper;
}
Смотрите Демо и GitHub Gist.