I'm trying to dynamically add a custom element I created, but it's not generating them properly.
div-item declaration:
class Item extends HTMLElement {
constructor() {
super();
this.src = this.getAttribute("src");
this.link = this.getAttribute("href");
this.attachShadow({ mode: 'open'});
this.text = this.textContent.trim();
this.shadowRoot.innerHTML = `
<style>/* Styles excluded */</style>
<div>
<a href='${this.link}'>
<div class="bg">
<img src="${this.src}" alt="${this.text}">
</div>
<p>${this.text}</p>
</a>
</div>
`;
}
}
customElements.define('div-item', Item);
Javascript generating:
function createDivItems() {
// Get all elements with the class name "row"
var rows = document.getElementsByClassName("row");
// Iterate over each row element
for (var j = 0; j < rows.length; j++) {
var row = rows[j];
// Remove all existing div-item elements from the row
row.innerHTML = '';
// Create and add elements based on the window width
for (var i = 0; i < (window.innerWidth - 60); i += 120) {
// Create a new custom element
var elem = document.createElement("div-item", {"src": '<?php echo "assets/not found.png"; ?>', "href": '<?php echo "/newPage.php"; -- placeholders for implementing the shop system ?>'});
// Set attributes for the custom element
elem.setAttribute("src", '<?php echo "assets/not found.png"; ?>');
elem.setAttribute("href", '<?php echo "/newPage.php"; ?>');
elem.innerHTML = 'not used.'
// Append the custom element to the current row
row.appendChild(elem);
}
}
}
I thought it would create the icons and text, but it just creates an empty square outline (from the CSS that was removed)
I put a delay on the creation of the custom tag and it generated them properly, but when I resized it became the empty square again.
Basically, I think when I create the tag it stops checking for the attributes, so I need to set the attributes when the element is created, not after it is.
Kaiido is right, you can not use the constructor
to do (some) DOM operations when you do
createElement("<div-item")
, because that element is in memory and only gets created in the DOM
when you do appendChild(elem)
appendChild
is also not what you want; use modern append
(appends multiple DOM nodes, but was not supported in good old Internet Explorer)
Because you do not want to call append(Child)
inside the loop, every call will probably cause DOM-repaints and reflows.
I focussed below code on the pain points; you can add your own stuff
Note: the connectedCallback
has a potentiual DOM issue when trying to read lightDOM,
not in this snippet.
98 out of 100 developers get it wrong because they don't know what the connectedCallback
does 100%
See my blogbost
<div id="row1" class="row">FOO</div>
<div id="row2" class="row">BAR</div>
<div id="row3" class="row">BAZ</div>
<script>
customElements.define('div-item', class extends HTMLElement {
connectedCallback() {
let src = this.getAttribute("src");
this.attachShadow({mode:'open'})
.innerHTML = `<img src="${src}" style="width:40px">` + this.textContent.trim();
}
});
var rows = [...document.getElementsByClassName("row")];
rows.map(row => {
row.innerHTML = ''; // delete FOO, BAR in row
const length = Math.ceil((window.innerWidth - 60) / 120);
console.log(row.id, "add", length, "div-items");
const items = Array(length).fill(0).map((_, index) => {
let elem = document.createElement("div-item");
elem.setAttribute("src", "//we-are-all.github.io/sheep.svg");
elem.append(row.id, "-", index);
return elem;
});
row.append(...items); // append to row ONCE!!
})
</script>