I have a fairly simple question. I need to create a list of items that is quite deeply nested, something of this sort (in reality every div has like 20+ classes, data-attributes and so forth):
<div class="one" data-foo="a x y z">
<div class="two" data-bar="x y z">
<ul class="three" data-foobar="a b c">
// contents
</ul>
</div>
</div>
My current code is:
const div1 = document.createElement("div");
div1.classList.add("one", all the other classes);
div1.setAttribute("data-foo", "lots of data attributes");
const div2 = document.createElement("div");
div2.classList.add("two", all the other classes);
div2.setAttribute("data-bar", "lots of data attributes");
const div3 = document.createElement("div");
div3.classList.add("three", all the other classes);
div3.setAttribute("data-foobar", "lots of data attributes");
div1.appendChild(div2);
div2.appendChild(div3);
// getting items for the list & other code
div3.appendChild(someOutputData);
I came up with a great idea of using a template literal so I don't have to add all the extra properties, classes etc. I know it seems like I could've fixed it with simple foreach
but there are bazillion of other things like aria tags etc. etc.
So my old code becomes:
const wrapper = document.createElement("div");
const layout = `<div class="one" data-foo="a x y z">
<div class="two" data-bar="x y z">
<ul class="three" data-foobar="a b c">
</ul>
</div>
</div>`;
wrapper.innerHTML = layout;
wrapper.appendChild(someOutputData);
Million times clearer, right?
The problem is wrapper.appendChild(someOutputData);
is not longer injecting data into .three
but into <div>
wrapper that is above .one
.
Is there some way I can target nested DOM elements created via template literals? How can I push my someOutputData
(list of nodes) into .three
using the second snippet? Also can I omit wrapper somehow? I don't really need the "div" around my list.
So use variables in your template and select the element you created to append the elements
function createComponent(data1, data2, data3, listData) {
const wrapper = document.createElement("div");
const layout = `<div class="one" data-foo="${data1}">
<div class="two" data-bar="${data2}">
<ul class="three" data-foobar="${data3}">
</ul>
</div>
</div>`;
wrapper.innerHTML = layout;
wrapper.querySelector("ul").append(...listData);
return wrapper;
}
const makeLi = (text) => {
const li = document.createElement('li');
li.textContent = text;
return li;
};
const lis = ['foo', 'bar'].map(makeLi);
const elem = createComponent(1,2,3, lis);
document.body.append(elem);
const lis2 = [1,2,3,4,5].map(makeLi);
const elem2 = createComponent(1,2,3, lis2);
document.body.append(elem2);