使用JavaScript包装一组DOM元素

32 javascript

我的p页面上有一系列标签,我想将它们全部包装到容器中,例如

<p>foo</p>
<p>bar</p>
<p>baz</p>
Run Code Online (Sandbox Code Playgroud)

我想将所有上述标签包装到容器中,如下所示:

<div>
    <p>foo</p>
    <p>bar</p>
    <p>baz</p>
</div>
Run Code Online (Sandbox Code Playgroud)

如何NodeList使用vanilla JavaScript 包装元素?

Kev*_*ski 29

以下发布的是jQuery wrapwrapAll方法的纯JavaScript版本.我无法保证它们的工作方式与jQuery 完全相同,但实际上它们的工作方式非常相似,应该能够完成相同的任务.它们可以使用单个HTMLElement或其中的数组.我还没有测试过确认,但它们都应该适用于所有现代浏览器(在某种程度上也适用于旧版本).

与选定的答案不同,这些方法通过使用insertBefore和appendChild来维护正确的HTML结构.

包裹:

// 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);
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

查看有关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);
    }
};
Run Code Online (Sandbox Code Playgroud)

查看有关jsFiddle 的工作演示.

  • 就像抬头一样,你最好不要扩展HTMLElement的原型,因为这很容易变得不安全并导致一些重大问题(我相信如果你使用的是jQuery).将其用作全局函数. (6认同)
  • 很棒的代码!只是:我认为最后一个 if 语句是多余的,因为如果兄弟为空/未定义,则 parent.insertBefore(this,sibling) 应该与 parent.appendChild(this) 完全相同。 (2认同)

Sar*_*raz 23

你可以这样做:

// 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);
Run Code Online (Sandbox Code Playgroud)

  • @KatieK从技术上讲,你应该首先在这里运行`removeChild`,但是在已经在DOM中的元素上运行`appendChild`会首先触发一个显式的`removeChild`调用(规范说所有的浏览器应该这样做,并且所有的都做afaik) . (3认同)
  • 我刚才注意到的这个脚本的一个问题(18个月之后)是它不会将现有的`<div>`包装在原位:它会将它们从文档中的任何位置删除,放入它们进入容器`<div>`,然后将容器`<div>`追加到文档的末尾(这不一定是`<div>`s最初的位置).这可能是理想的行为,但我想我会指出它. (3认同)
  • 很好的答案,但是每个浏览器都支持一个挑剔/问题 - "document.body",并且搜索DOM以查找所有正文实例并通过数组索引进行选择肯定更有效率? (2认同)
  • @lucideer:"document.body ...肯定更有效率"PREMATURE OPTIMIZATION,我希望在所有现代浏览器中看到你的`document.body`与`document.getElementsByTagName('body')`基准的截图立即:) (2认同)
  • @保罗-哈。Touché,但我不太喜欢教条式的断言,比如关于优化不成熟的法律——如果某些东西明显更有效而不损害可读性、可维护性、可移植性等……yadayada。`document.body` 至少不那么冗长。而且更漂亮。对于漂亮的代码有很多话要说。 (2认同)

pmr*_*ule 5

这是我的wrap() 的javascript 版本。更短,但您必须在调用函数之前创建元素。

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);
}
Run Code Online (Sandbox Code Playgroud)
div {
  border: 2px solid #f00;
  margin-bottom: 10px;
}
Run Code Online (Sandbox Code Playgroud)
<ul id="elementID">
  <li>Chair</li>
  <li>Sofa</li>
</ul>

<button onclick="wrapDiv()">Wrap the list</button>
Run Code Online (Sandbox Code Playgroud)


Web*_*ner 5

wrapAll凯文的答案开始并解决了以下问题以及他的回答下方的评论中提到的问题,从而达到了此功能。

  1. 他的函数尝试将包装器附加到传递的nodeList中第一个节点的下一个同级对象。如果该节点也位于nodeList中,那将是有问题的。要查看实际效果,请<li>他的wrapAll演示中从第一个到第二个之间删除所有文本和其他元素。
  2. 与该声明相反,由于使用的循环技术,如果在数组而不是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;
}
Run Code Online (Sandbox Code Playgroud)

参见DemoGitHub Gist

  • 很棒的解决方案。为了实现多功能性,我如何更改它以接受单个节点而不是节点列表?它在“nodes[0].parentNode;”处出错。 (2认同)