Reputation: 983
I'm wondering if there is an "easy" way to create a big DOM object by specifying the attributes in a json?
I know it's possible to do this with appendChild and/or innerHTML, but for this object in question, it looks quite messy.
The goal is to create this HTML object from the bottom up in javascript:
<div class="p-2">
<div class="d-flex p-2 bg-dark2-35">
<div class="Popover h-3">
<div class="pfp" style="background-image: url('/data/images/PP/1.png')"></div>
</div>
<div class="d-grid ms-2 w-100">
<p><b class="lead text-blue">Username</b> — <i>2020-01-16 19:29:34</i></p>
<p class="text-white">some comment text</p>
</div>
</div>
</div>
I was wondering if it's possible to do something like this (I know it doesn't work):
let comment = document.getElementById("comment-section");
let elm = {
className: "p-2",
appendChild(node) {
classList.add("d-flex", "p-2", "bg-dark2-35"),
appendChild(node2){
classList.add("Popover", "h-3"),
// ... and so on
}
}
}
comment.appendChild(elm);
Is there an easy way to do this in pure JS? Would I have to make a function to achieve this? or maybe go as far as to import a library?
The question is a bit similar to Create DOM element from Object in javascript, but I'm completely lost when it comes to this many childrens, with childrens and so on
I'm a bit of a newbie when it comes to JavaScript. The question might come off as strange
Upvotes: 0
Views: 1095
Reputation: 983
Coming back to this question, check out this function:
/**
* A "fancier" function to make a HTML element
* @param nodeName
* @param elementOptions
*/
function createElement(nodeName, elementOptions) {
if (!nodeName && !elementOptions) return;
if (!nodeName) nodeName = elementOptions.nodeName;
// Defining the new element
let elm = document.createElement(nodeName);
if(elementOptions?.href){
elm.setAttribute("href", elementOptions.href); // Use setAttribute to set the href attribute
}
// Assign classes if defined
if (elementOptions?.classList)
for (let i = 0; i < elementOptions.classList.length; i++)
if (elementOptions.classList[i] && elementOptions.classList[i]?.trim() !== "")
elm.classList.add(elementOptions.classList[i]);
// Assign id if defined
if (elementOptions?.id) elm.id = elementOptions["id"];
// Assign title if defined
if (elementOptions?.title) elm.title = elementOptions["title"];
// Assign tabIndex if defined
if (elementOptions?.tabIndex) elm.tabIndex = elementOptions["tabIndex"];
// Assign innerHTML if defined
if (elementOptions?.innerHTML) elm.innerHTML = elementOptions["innerHTML"];
// Assign style if defined
if (elementOptions?.style) {
Object.entries(elementOptions.style).forEach(([property, value]) => {
elm.style[property] = value;
});
}
if (elementOptions?.events) {
for (const [eventName, eventListener] of Object.entries(elementOptions.events)) {
if (eventName in elm) {
elm.addEventListener(eventName, eventListener);
}else{
console.warn(`Invalid event name: ${eventName}`);
}
}
}
// Assign attributes if defined
if (elementOptions?.attributes) {
Object.entries(elementOptions.attributes).forEach(([property, value]) => {
elm.setAttribute(property, value);
});
}
// Lastly do recursion to create children
if (elementOptions?.children){
for (let i = 0; i < elementOptions.children.length; i++) {
let child = createElement(null, elementOptions.children[i])
if (child) elm.appendChild(child)
}
}
return elm;
}
And it can be created like so:
const myElm = createElement("DIV", {
classList: ["p-2"],
children: [{
nodeName: "DIV",
classList: ["d-flex", "p-2", "bg-dark2-35"],
children: [
{
nodeName: "DIV",
classList: ["Popover", "h-3"],
children: [{
nodeName: "DIV",
classList: ["pfp"],
style: {
backgroundImage: "url('/data/images/PP/1.png')"
}
}]
},
{
nodeName: "DIV",
classList: ["d-grid", "ms-2", "w-100"],
children: [
{
nodeName: "P",
innerHTML: `<b class="lead text-blue">${username}</b> — <i>${timestamp}</i>`
},
{
nodeName: "P",
classList: ["text-white"],
innerHTML: "some comment text"
}
]
}
]
}]
})
Overall this is a bit messy compared to something that React can achieve or just using template literals.
Upvotes: 0
Reputation: 143
You can use template literals Mdn docs - Template Literals
const selector = document.getElementById('comment-section');
// By using createElement you can deal with loops, event listener, etc..
const comment = document.createElement('div');
comment.classList.add("p-2");
const username = "TypeWar";
// Template literals
comment.innerHTML = `
<div class="d-flex p-2 bg-dark2-35">
<div class="Popover h-3">
<div class="pfp" style="background-image: url('/data/images/PP/1.png')"></div>
</div>
<div class="d-grid ms-2 w-100">
<p><b class="lead text-blue">${username}</b> — <i>2020-01-16 19:29:34</i></p>
<p class="text-white">some comment text</p>
</div>
</div>
`;
selector.appendChild(comment);
<div id="comment-section">
</div>
Upvotes: 1