patternjavascriptMinor
Adding a replica of some DOM elements on click
Viewed 0 times
clickelementsreplicaaddingdomsome
Problem
I have the following code which inserts a new element of the same type over and over again to the parent element. I am new to JavaScript so was wondering if there was a better way of doing this. I apologise for the variable names, I'll get around to changing those later, just allows me to see the flow better. I'm trying to write this in pure js.
//Add a new lobby element
document.getElementById("addLobby").addEventListener("click", function(e) {
var layer1 = document.createElement("div");
layer1.setAttribute("class","col-wd-12 col-md-6 col-sm-4");
var layer2 = document.createElement("div");
layer2.setAttribute("class","col lobby");
var layer3 = document.createElement("div");
layer3.setAttribute("class","col lobby-infoPanel");
var layer3v2 = document.createElement("div");
layer3v2.setAttribute("class","overlay lobby-overlay");
var layer3v2v1 = document.createElement("a");
layer3v2v1.setAttribute("href","#");
var l3v2v1v1 = document.createElement("span");
l3v2v1v1.setAttribute("class", "overlay-options lobby-overlay-options");
var layer4 = document.createElement("div");
layer4.setAttribute("class","col lobby-info-count");
var layer4v2 = document.createElement("div");
layer4v2.setAttribute("class","col lobby-info-count lobby-info-users");
var l3v2v1v1Text = document.createTextNode("+");
var l4text = document.createTextNode("5/5");
var l4v2text = document.createTextNode("@User1");
layer4v2.appendChild(l4v2text);
layer4.appendChild(l4text);
layer3.appendChild(layer4);
layer3.appendChild(layer4v2);
l3v2v1v1.appendChild(l3v2v1v1Text);
layer3v2v1.appendChild(l3v2v1v1);
layer3v2.appendChild(layer3v2v1);
layer2.appendChild(layer3);
layer2.appendChild(layer3v2);
layer1.appendChild(layer2);
var lastNode = document.getElementById("addLobby").parentElement;
lastNode.parentElement.insertBefore(layer1, lastNode);
});Solution
First suggestion
One first refactoring idea is to create a function that creates an element, sets its attributes and adds it to a parent element.
You could also separate your structure from your DOM manipulation.
I suggest you create your structure as a JSON, and then create the nodes recursively.
For example, you could define your structure as:
and then:
Here is a jsfiddle with the whole working example.
Second suggestion (less JS)
You could create a hidden HTML 'template' and display it when needed.
You can set:
-
-
or
in css (more info here) directly in the style attribute or through a class. On the click event you make this element visible.
The HTML:
And then in the JS:
Here is the second fiddle.
If you need to add the template multiple times, or if you have multiple templates on your page, you should define them at the end on the page.
When needed, clone the template and append it to the desired parent.
Conclusion
If you don't have a particular need to create the elements in the JS, I think the second solution it's more elegant (and less code ;) )
One first refactoring idea is to create a function that creates an element, sets its attributes and adds it to a parent element.
function createElement(elementType, classes, parent) {
var e = document.createElement(e);
e.setAttribute('class', classes);
parent.appendChild(e);
return e;
}You could also separate your structure from your DOM manipulation.
I suggest you create your structure as a JSON, and then create the nodes recursively.
For example, you could define your structure as:
{
element: 'div',
class: 'col-wd-12 col-md-6 col-sm-4',
children: [{
element: 'div',
class: 'col loby',
children: [
//...
]
},
// ...
}and then:
function createDOMElements(data) {
// ...
// create the element
var elem = document.createElement(data.element);
// add its attributes
for (var attribute in data) {
if (data.hasOwnProperty(attribute) && attribute !== 'element' && attribute !== 'children') {
var attributeValue = data[attribute];
elem.setAttribute(attribute, attributeValue);
}
}
// ...
// add the children (forEach is from ES5 on)
data.children.forEach(function(child) {
// recursive call to create child elements
var childElement = createDOMElements(child);
elem.appendChild(childElement);
});
return elem;
}Here is a jsfiddle with the whole working example.
Second suggestion (less JS)
You could create a hidden HTML 'template' and display it when needed.
You can set:
-
visibility to hidden if you still want to affect the layout (take the space needed)-
or
display to nonein css (more info here) directly in the style attribute or through a class. On the click event you make this element visible.
The HTML:
And then in the JS:
document.getElementById("addLobby").addEventListener("click", function(e) {
document.getElementById("template").style.display = "initial";
});Here is the second fiddle.
If you need to add the template multiple times, or if you have multiple templates on your page, you should define them at the end on the page.
When needed, clone the template and append it to the desired parent.
Conclusion
If you don't have a particular need to create the elements in the JS, I think the second solution it's more elegant (and less code ;) )
Code Snippets
function createElement(elementType, classes, parent) {
var e = document.createElement(e);
e.setAttribute('class', classes);
parent.appendChild(e);
return e;
}{
element: 'div',
class: 'col-wd-12 col-md-6 col-sm-4',
children: [{
element: 'div',
class: 'col loby',
children: [
//...
]
},
// ...
}function createDOMElements(data) {
// ...
// create the element
var elem = document.createElement(data.element);
// add its attributes
for (var attribute in data) {
if (data.hasOwnProperty(attribute) && attribute !== 'element' && attribute !== 'children') {
var attributeValue = data[attribute];
elem.setAttribute(attribute, attributeValue);
}
}
// ...
// add the children (forEach is from ES5 on)
data.children.forEach(function(child) {
// recursive call to create child elements
var childElement = createDOMElements(child);
elem.appendChild(childElement);
});
return elem;
}<div id="template" class="col-wd-12 col-md-6 col-sm-4" style="display:none;">
<!-- all the children -->
</div>document.getElementById("addLobby").addEventListener("click", function(e) {
document.getElementById("template").style.display = "initial";
});Context
StackExchange Code Review Q#124392, answer score: 4
Revisions (0)
No revisions yet.