【问题标题】:Append multiple items in JavaScript在 JavaScript 中附加多个项目
【发布时间】:2016-08-16 07:57:13
【问题描述】:

我有以下功能,我正在尝试找出一种更好的方法来使用appendChild() 附加多个项目。

当用户点击添加时,每个项目应如下所示:

<li>
  <input type="checkbox">
  <label>Content typed by the user</label>
  <input type="text">
  <button class="edit">Edit</button>
  <button class="delete">Delete</button>
</li>

我有这个功能来添加这些元素:

function addNewItem(listElement, itemInput) {
  var listItem = document.createElement("li");
  var listItemCheckbox = document.createElement("input");
  var listItemLabel = document.createElement("label");
  var editableInput = document.createElement("input");
  var editButton = document.createElement("button");
  var deleteButton = document.createElement("button");

  // define types
  listItemCheckbox.type = "checkbox";
  editableInput.type = "text";

  // define content and class for buttons
  editButton.innerText = "Edit";
  editButton.className = "edit";
  deleteButton.innerText = "Delete";
  deleteButton.className = "delete";

  listItemLabel.innerText = itemText.value;

  // appendChild() - append these items to the li
  listElement.appendChild(listItem);
  listItem.appendChild(listItemCheckbox);
  listItem.appendChild(listItemLabel);
  listItem.appendChild(editButton);
  listItem.appendChild(deleteButton);

  if (itemText.value.length > 0) {
    itemText.value = "";
    inputFocus(itemText);
  }
}

但是你可以注意到我重复了三遍appendChild()listItem。是否可以向appendChild() 添加多个项目?

【问题讨论】:

标签: javascript html appendchild


【解决方案1】:

您可以使用DocumentFragment 完成此操作。

var documentFragment = document.createDocumentFragment();
documentFragment.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
listElement.appendChild(documentFragment);

DocumentFragments 允许开发人员将子元素放置到 任意类似节点的父节点,允许类似节点的交互 没有真正的根节点。这样做可以让开发人员制作 在可见 DOM 中不这样做的结构

【讨论】:

  • 但是这些appendChild 调用都不会发生在实时 DOM 中,因此浏览器不会在添加每个子元素后尝试重新渲染整个 DOM。相反,在附加整个片段后只发生一次重新渲染。
  • 很好的答案 - DocumentFragment 是对渲染的 DOM 进行批量更改的一种非常有用的方法。
  • 这应该是公认的答案,因为它使用 DOM API 而不是字符串。很酷的答案。谢谢。
  • 哇,谢谢!这正是我想要的!
【解决方案2】:

您可以在 JavaScript 中使用append 方法。

这类似于 jQuery 的 append 方法,但不支持 IE 和 Edge。

您可以更改此代码

listElement.appendChild(listItem);
listItem.appendChild(listItemCheckbox);
listItem.appendChild(listItemLabel);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);

listElement.append(listItem,listItemCheckbox,listItemLabel,editButton,deleteButton);

【讨论】:

  • 这真是太棒了,可惜 IE 仍然不支持它。
  • 截至 2019 年 1 月,append 得到广泛支持,但它不是标准化的,被认为是一种实验性技术。应该谨慎使用
  • @skzryzg 我认为我从mdn 看到的不再是实验性的。
【解决方案3】:

就我个人而言,我不明白你为什么要这样做。

但如果你真的需要用一个语句替换所有appendChild(),你可以将创建元素的outerHTML分配给li元素的innerHTML

您只需要替换以下内容:

  listElement.appendChild(listItem);
  listItem.appendChild(listItemCheckbox);
  listItem.appendChild(listItemLabel);
  listItem.appendChild(editButton);
  listItem.appendChild(deleteButton);

以下内容:

listItem.innerHTML+= listItemCheckbox.outerHTML + listItemLabel.outerHTML + editButton.outerHTML + deleteButton.outerHTML;
listElement.appendChild(listItem);

说明:

元素 DOM 接口的 outerHTML 属性获取描述元素及其后代的序列化 HTML 片段。因此,将创建的元素的outerHTML 分配给li 元素的innerHTML 类似于将它们附加到它。

【讨论】:

  • 这实际上并没有附加孩子,而是他们的副本,这会丢失属性。
  • @MichaelScottCuthbert 丢失了哪些属性?我不明白。它做了应该做的事情。
  • 设置一个像listCheckbox.asdf = 5; listItem.innerHTML = listCheckbox.outerHTML; listItem.querySelector('input').asdf === 5这样的属性,它会是假的,因为在outerHTML中找不到属性(与属性不同)。对于输入项,丢失的最重要的属性是.value(不同于属性“值”)。还有事件侦听器等。出于多种目的(包括此处),您的解决方案可以工作,但它确实会丢失属性并且速度要慢得多。
  • @MichaelScottCuthbert 是的,我明白你的意思是对的,但我的意思是这里只需要处理 HTML 对象树。
  • 我认为总体上应该避免这种情况。如果您注册任何事件侦听器或对 DOM 元素的引用,所有这些都将在使用 innerHTML 时丢失,这可能会导致您的应用程序未来出现意外行为。
【解决方案4】:

合并@Atrahasis 和@Slavik 的答案:

if (Node.prototype.appendChildren === undefined) {
  Node.prototype.appendChildren = function() {
    let children = [...arguments];

    if (
      children.length == 1 &&
      Object.prototype.toString.call(children[0]) === "[object Array]"
    ) {
      children = children[0];
    }

    const documentFragment = document.createDocumentFragment();
    children.forEach(c => documentFragment.appendChild(c));
    this.appendChild(documentFragment);
  };
}

这接受子作为多个参数,或作为单个数组参数:

foo.appendChildren(bar1, bar2, bar3);
bar.appendChildren([bar1, bar2, bar3]);

更新 - 2020 年 6 月

目前大多数浏览器都支持append"spread operator"

上面的调用可以重写为:

foo.append(bar1, bar2, bar3);
bar.append(...[bar1, bar2, bar3]);

【讨论】:

  • P.S.仅限 ES6+,我相信这可以用更好的方式编写。将节点分散到一个数组中/只接受一个数组作为单数参数代码路径(主要是为了使用forEach)可能很糟糕。此外,向 Node.prototype/similar 添加函数可能是您不应该做的事情。
  • 我将 polyfill 包装在存在条件中。我认为将它添加到Node 是很好的,因为Node 是定义appendChild 的对象。尽管Sagar V's 响应是最好的方式,尤其是在传播运营商的支持下。我在您的答案末尾添加了更新版本。
【解决方案5】:

你需要追加几个孩子吗?只需使用 appendChildren 将其设为复数即可!

第一件事:

HTMLLIElement.prototype.appendChildren = function () {

  for ( var i = 0 ; i < arguments.length ; i++ )

    this.appendChild( arguments[ i ] );

};

然后对于任何列表元素:

listElement.appendChildren( a, b, c, ... );

//check :
listElement.childNodes;//a, b, c, ...

当然适用于每个具有appendChild 方法的元素!喜欢HTMLDivElement

【讨论】:

  • 最好将它与 Slavik 的答案中的 DocumentFragment 结合起来,以将多个 appendChild 调用排除在主 DOM 之外。
  • @AndFinally - 真的很好奇/不确定:为什么?重复添加到片段比 DOM 更便宜?
  • @RockinSocks 将一系列节点附加到片段中,并且仅在将所有内容放在一起后才附加片段,以避免浏览器必须对页面上元素的位置和大小进行多次重新计算,即进行多次重排.这样,当您将片段附加到活动 DOM 时,重排只会发生一次。因此,更改应该更快地呈现。请参阅此处以获取快速说明 - sitepoint.com/10-ways-minimize-reflows-improve-performance
  • @AndFinally 这对现代浏览器来说真的是这样吗?如果在单次执行中发生一系列 DOM 更改,我会想象浏览器将足够聪明地保存更新。
【解决方案6】:

你可以使用createContextualFragment,它返回一个从字符串创建的documentFragment。 如果您必须构建多个Nodes 并将其附加到现有的Element 上,这是完美的,因为您可以将其全部添加而没有innerHTML 的缺点

https://developer.mozilla.org/en-US/docs/Web/API/Range/createContextualFragment

// ...
var listItem = document.createElement("li");
var documentFragment = document.createRange().createContextualFragment(`
    <input type="checkbox">
    <label>Content typed by the user</label>
    <input type="text">
    <button class="edit">Edit</button>
    <button class="delete">Delete</button>
`)
listItem.appendChild(documentFragment)
// ...

【讨论】:

    【解决方案7】:

    您可以像这样将元素分组到单个 innerHTML 组中:

      let node = document.createElement('li');
      node.innerHTML = '<input type="checkbox"><label>Content typed by the user</label>  <input type="text"><button class="edit">Edit</button><button class="delete">Delete</button>';
      document.getElementById('orderedList').appendChild(node);
    

    那么 appendChild() 只使用一次。

    【讨论】:

    • 请注意,这样做将删除附加到附加节点的任何事件侦听器,因为与 appendChild 不同,innerHTML 不会保留这些。
    • @ojathelonius,这无关紧要:节点在此处创建,因此它没有任何事件监听器。
    • 我说的不是这里创建的节点,而是它的内容。 innerHTML 不保留任何附加的事件监听器。
    【解决方案8】:

    让我们试试这个:

    let parentNode = document.createElement('div');
    
    parentNode.append(...[
        document.createElement('div'),
        document.createElement('div'),
        document.createElement('div'),
        document.createElement('div'),
        document.createElement('div')
    ]);
    
    console.log(parentNode);
    

    【讨论】:

      【解决方案9】:

      如果您使用内置的arguments object,则可以编写自己的函数

      function appendMultipleNodes(){
        var args = [].slice.call(arguments);
        for (var x = 1; x < args.length; x++){
            args[0].appendChild(args[x])
        }
        return args[0]
      }
      

      然后你会这样调用函数:

      appendMultipleNodes(parent, nodeOne, nodeTwo, nodeThree)
      

      【讨论】:

        【解决方案10】:

        这是一个快速修复

        document.querySelector("#parentid .parenClass").insertAdjacentHTML('afterend', yourChildElement.outerHTML);
        

        【讨论】:

          【解决方案11】:

          伙计们,我真的推荐你使用这个。

          [listItemCheckbox, listItemLabel, editButton, deleteButton]
          .forEach((item) => listItem.appendChild(item));

          因为你不能一次追加多个孩子。我觉得这个更好看。

          【讨论】:

            【解决方案12】:

            我想补充一点,如果你想给你的 html 添加一些可变性,你也可以像这样添加变量:

            let node = document.createElement('div');
            node.classList.add("some-class");
            node.innerHTML = `<div class="list">
                              <div class="title">${myObject.title}</div>
                              <div class="subtitle">${myObject.subtitle}
                            </div>`;
            

            【讨论】:

              猜你喜欢
              • 2014-12-17
              • 1970-01-01
              • 1970-01-01
              • 2020-04-22
              • 1970-01-01
              • 2012-10-22
              • 1970-01-01
              • 2016-01-23
              • 2022-01-20
              相关资源
              最近更新 更多