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.
2
Answers
The constructor is called inside the
createElement()
call (or during upgrade if the element was parser inserted).So by the time you call the
setAttribute()
method, the constructor has already been called and your element’s shadow has already been built, without the attribute values.Instead you want to the shadow content of your element during its
connectedCallback
:Kaiido is right, you can not use the
constructor
to do DOM operations when you docreateElement("<div-item")
, because that element is in memory and only gets created in the DOMwhen you do
appendChild(elem)
appendChild
is also not what you want; use modernappend
(not supported in good old Internet Explorer)And do not 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. See my blogbost