While you've already accepted an answer, I felt that it might be worth offering a more extensible approach, which allows you to use unobtrusive JavaScript (rather than relying upon in-line event-handlers such as onclick
) for easier maintenance.
It's also a little more extensible and customisable:
// a simple function to help derive the correct element
// from the supplied argument, 'needle':
function derive(needle) {
// if the needle has a nodeType and if that nodeType is
// exactly equal to 1:
if (needle.nodeType && needle.nodeType === 1) {
// the needle is then an element-node, and here
// we convert that node into an Array of one:
needle = [needle];
// otherwise, if the needle is a string, and
// document.getElementById() finds an element
// with that id:
} else if ('string' === typeof needle && document.getElementById(needle)) {
// we find that element-node again, using the string
// and again convert it to an Array of one:
needle = [document.getElementById(needle)];
// otherwise, if the needle is - again - a string, and
// document.querySelectorAll() can find a collection
// (of one or more) elements matching the selector that
// the needle is implied to be then we retrieve those
// elements and, using Array.from(), we convert the
// collection into an Array:
} else if ('string' === typeof needle && document.querySelectorAll(needle)) {
needle = Array.from(document.querySelectorAll(needle));
}
// here we return the results to the calling context:
return needle;
}
function addNewElement(opts) {
// the default settings for the function:
// append: Boolean, true: the content will be
// inserted after the found sibling-
// node; false: the content will be
// inserted before the found sibling-
// node.
// classes: String, a string of white-space
// separated class-names to add to
// the new contents,
// Array, an array of class-names to
// add to the new contents.
// content: String, a string of HTML you wish
// to appear in the newly-added content.
// count: Number, the number of elements you
// wish to insert at once.
// create: String, the element-type to create
// null, if you want the function to
// 'decide' for itself.
// parent: Node, the element to which you want
// to add new elements,
// String, the id of the element to
// which you want to add new elements,
// or a CSS selector by which you want
// find the element(s) in the document
// to add new elements to.
// sibling: Node, the node beside which the new
// element(s) should be added.
// Null, the function will try to determine
// the desired element beside which the
// content should be added, based on
// the 'append' setting (above).
var settings = {
'append': true,
'classes' : null,
'content': 'Newly-added element.',
'count': 1,
'create': null,
'parent': document.body,
'sibling': null
},
// uninitialised variables for use later, primarily
// to declare/instantiate variables in one place:
parents,
childType,
created,
sibling,
clone,
classes,
count,
// a documentFragment to enable the addition of multiple
// elements at the same time without triggering (quite so)
// many redraws of the document/page:
fragment = document.createDocumentFragment();
// using Object.keys to iterate over the opts Object, if
// one is supplied or an empty object to avoid errors,
// using the Array.prototype.forEach() method:
Object.keys(opts || {}).forEach(function(key) {
// here we update/overwrite the keys of the
// settings object to the values held in those
// properties of the opts Object:
settings[key] = opts[key];
});
// we call the derive function to retrieve an array
// of element(s):
parents = derive(settings.parent);
// checking, and then storing, the value of
// settings.append; it it's equal to true the
// assessment returns true, if it's equal to
// false the assessment returns false (this
// is a naive check, because it requires that
// a Boolean is stored in that property):
appendCheck = settings.append === true;
// ensuring that the settings.count number
// is a number by parsing the potential
// String, other-based number, into base-10:
count = parseInt(settings.count, 10);
// iterating over each of the parents:
parents.forEach(function(pater) {
// 'pater' the first argument is a reference
// to the current array-element of the array
// over which we're iterating.
// retrieving the element-type to be created,
// if a value was supplied in settings.create
// then we use that (we don't check it's a
// valid element, or that it can be validly
// contained in the nominated parent), otherwise
// if the current element node has children
// then we retrieve the localName of its
// lastElementChild, if it has no children
// the ternary returns null and we move to
// the string of 'div':
childType = settings.create || (pater.children.length > 0 ? pater.lastElementChild.localName : null) || 'div';
// here we create the element:
created = document.createElement(childType);
// if the earlier assessment of settings.append
// resulted in true:
if (appendCheck === true) {
// we find the sibling beside which to insert the
// new content; if a node was supplied we use that,
// otherwise we use the lastElementChild or lastChild:
sibling = settings.sibling || pater.lastElementChild || pater.lastChild;
} else if (appendCheck === false) {
// otherwise, we use either the supplied value or
// we use the firstElementChild or firstChild:
sibling = settings.sibling || pater.firstElementChild || pater.firstChild
}
// assign the supplied - or default - content to the
// created element:
created.innerHTML = settings.content;
// if any class-names have been supplied:
if (settings.classes) {
// we first check whether the settings.classes
// variable is an Array (using Array.isArray),
// which returns a Boolean (true or false); if
// it returns true we simply use the Array otherwise
// we assume it's a String and split that String
// on its white-space characters (/\s+/):
classes = Array.isArray(settings.classes) ? settings.classes : settings.classes.split(/\s+/);
// iterating over the array of class-names:
classes.forEach(function(cN) {
// the first argument (cN) is a reference
// to the current array-element of the
// Array over which we're iterating.
// here we use the Element.classList API to
// add each of the class-names:
created.classList.add(cN);
});
}
// a simple for loop to add the desired
// number of new elements (as supplied in
// the settings.count, or opts.count
// setting):
for (var i = 0; i < count; i++) {
// clone the created-element (and its
// child elements):
clone = created.cloneNode(true);
// append the cloned node to the
// documentFragment we created
// earlier:
fragment.appendChild(clone);
}
// here we use parentNode.insertBefore() to insert
// the new contents (held in fragment) either the
// sibling.nextSibling (if appendCheck is true) or
// before the sibling (if appendCheck is false):
pater.insertBefore(fragment, (appendCheck ? sibling.nextSibling : sibling));
});
}
// retrieving the <button> elements on the page, and converting
// to an Array, using Array.from():
var buttons = Array.from(document.querySelectorAll('button'));
// iterating over those <button> elements in the Array:
buttons.forEach(function(button) {
// using the anonymous function of the addEventListener()
// to call the addNewElement function, in which
// we set the opts.parent setting to the
// previousElementSibling of the button
// firing the event:
button.addEventListener('click', function() {
addNewElement({
'parent': button.previousElementSibling
});
});
});
function derive(needle) {
if (needle.nodeType && needle.nodeType === 1) {
needle = [needle];
} else if ('string' === typeof needle && document.getElementById(needle)) {
needle = [document.getElementById(needle)];
} else if ('string' === typeof needle && document.querySelectorAll(needle)) {
needle = Array.from(document.querySelectorAll(needle));
}
return needle;
}
function addNewElement(opts) {
var settings = {
'append': true,
'classes': null,
'create': null,
'content': 'Newly-added element.',
'count': 1,
'parent': document.body,
'sibling': null
},
parents,
childType,
created,
sibling,
clone,
classes,
fragment = document.createDocumentFragment();
Object.keys(opts || {}).forEach(function(key) {
settings[key] = opts[key];
});
parents = derive(settings.parent);
appendCheck = settings.append === true;
parents.forEach(function(pater) {
childType = settings.create || (pater.children.length > 0 ? pater.lastElementChild.localName : null) || 'div';
created = document.createElement(childType);
if (appendCheck === true) {
sibling = settings.sibling || pater.lastElementChild || pater.lastChild;
} else if (appendCheck === false) {
sibling = settings.sibling || pater.firstElementChild || pater.firstChild
}
created.innerHTML = settings.content;
if (settings.classes) {
classes = Array.isArray(settings.classes) ? settings.classes : settings.classes.split(/\s+/);
classes.forEach(function(cN) {
created.classList.add(cN);
});
}
for (var i = 0; i < settings.count; i++) {
clone = created.cloneNode(true);
fragment.appendChild(clone);
}
pater.insertBefore(fragment, (appendCheck ? sibling.nextSibling : sibling));
});
}
var buttons = Array.from(document.querySelectorAll('button'));
buttons.forEach(function(button) {
button.addEventListener('click', function() {
addNewElement({
'parent': button.previousElementSibling
});
});
});
div {
border: 2px solid #eeeeee;
background-color: #dff0d8;
}
ol {
background-color: #dff0d8;
}
li {
background-color: #eff0c8;
}
<p>In next example we can add new child element to this list:</p>
<ol id="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ol>
<button>Add new li-element to this list</button>
<p>In next example we can add new child element to this div:</p>
<div id="someThing">Something here</div>
<button>Add new div-element to this div</button>
JS Fiddle demo.
References: