brunodd
brunodd

Reputation: 2584

Append multiple items in JavaScript

I have the following function and I am trying to figure out a better way to append multiple items using appendChild().

When the user clicks on Add, each item should look like this:

<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>

and I have this function to add these elements:

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);
  }
}

But you can notice that I am repeating three times the appendChild() for listItem. Is it possible to add multiple items to the appendChild() ?

Upvotes: 57

Views: 217838

Answers (15)

Daniel Brooks
Daniel Brooks

Reputation: 1087

Great way to dynamically add elements to a webpage. This function takes 3 arguments, 1 is optional. The wrapper will wrap the parent element and it's elements inside another element. Useful when creating tables dynamically.

  function append(parent, child, wrapper="") {
    if (typeof child == 'object' && child.length > 1) {
      child.forEach(c => {
        parent.appendChild(c);
      });
    } else {
      parent.appendChild(child);
    }
    if (typeof wrapper == 'object') {
      wrapper.appendChild(parent);
    }
  }

Upvotes: 0

aderchox
aderchox

Reputation: 4074

Also here's a helper function that uses the fragment technique as introduced in the @Slavik's answer and merges it with DOMParser API:

function createHtmlFromString(stringHtml) {
  const parser = new DOMParser();
  const htmlFragment = document.createDocumentFragment();
  const children = parser.parseFromString(stringHtml, "text/html").body
    .children;
  htmlFragment.replaceChildren(...children);
  return htmlFragment;
}

Now to append multiple children with this, you can make the code much more readable and brief, e.g.:

  const htmlFragment = createHtmlFromString(`<div class="info">
      <span></span>
      <h2></h2>
      <p></p>
      <button></button>
    </div>
    <div class="cover">
      <img />
    </div>
  `);

Here's also a working example of these used in action: example link.

Note1: You could add text content in the above tags too and it works, but if it's data from user (or fetched from API), you'd better not trust it for better security. Instead, first make the fragment using the above function and then do something like this:

  htmlFragment.querySelector(".info > span").textContent = game.name;

Note2: Don't use innerHTML to insert HTML, it is unsecure.

Upvotes: 1

Raki Lachraf
Raki Lachraf

Reputation: 147

Why isn't anybody mentioning the element.append() function ?!

you can simply use it to append multiple items respectively as so:

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

Upvotes: 7

Bushra Mustofa
Bushra Mustofa

Reputation: 1429

Guys I really recommend you to use this one.

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

Since you can't append multiple children at once. I think this one looks better.

Upvotes: 0

Abhishek Kushwaha
Abhishek Kushwaha

Reputation: 1519

This is a quick fix

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

Upvotes: 0

Marek Lisiecki
Marek Lisiecki

Reputation: 726

Let's try this:

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);

Upvotes: 7

Pend
Pend

Reputation: 752

Merging the answers by @Atrahasis and @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);
  };
}

This accepts children as multiple arguments, or as a single array argument:

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

Update – June 2020

Most all current browsers support append and the "spread operator" now.

The calls above can be re-written as:

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

Upvotes: 15

asdru
asdru

Reputation: 1264

You can use createContextualFragment, it return a documentFragment created from a string. It is perfect if you have to build and append more than one Nodes to an existing Element all together, because you can add it all without the cons of 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)
// ...

Upvotes: 6

J C
J C

Reputation: 771

I would like to add that if you want to add some variability to your html, you can also add variables like this:

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>`;

Upvotes: -1

Simon Suh
Simon Suh

Reputation: 10892

You could just group the elements into a single innerHTML group like this:

  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);

then appendChild() is only used once.

Upvotes: 3

cнŝdk
cнŝdk

Reputation: 32165

Personally, I don't see why you would do this.

But if you really need to replace all the appendChild() with one statement, you can assign the outerHTML of the created elements to the innerHTML of the li element.

You just need to replace the following:

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

With the following:

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

Explanation:

The outerHTML attribute of the element DOM interface gets the serialized HTML fragment describing the element including its descendants. So assigning the outerHTML of the created elements to the innerHTML of the li element is similar to appending them to it.

Upvotes: 15

Ricardo Pierre-Louis
Ricardo Pierre-Louis

Reputation: 184

It's possible to write your own function if you use the built in 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]
}

Then you would call the function as such:

appendMultipleNodes(parent, nodeOne, nodeTwo, nodeThree)

Upvotes: 2

Sagar V
Sagar V

Reputation: 12478

You can use the append method in JavaScript.

This is similar to jQuery's append method but it doesnot support IE and Edge.

You can change this code

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

to

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

Upvotes: 70

Mouloud85
Mouloud85

Reputation: 4234

You need to append several children ? Just make it plural with appendChildren !

First things first :

HTMLLIElement.prototype.appendChildren = function () {

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

    this.appendChild( arguments[ i ] );

};

Then for any list element :

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

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

Works with every element that has the appendChild method of course ! Like HTMLDivElement.

Upvotes: 5

Slavik
Slavik

Reputation: 6837

You can do it with DocumentFragment.

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

DocumentFragments allow developers to place child elements onto an arbitrary node-like parent, allowing for node-like interactions without a true root node. Doing so allows developers to produce structure without doing so within the visible DOM

Upvotes: 115

Related Questions