I'm trying to grab an element from page and add an addEventHandler to it. Element (link with class .catalog__link) was dynamically created in another external function when the page was loaded using insertAdjacentHTML. I invoke them both in third js file via import keyword. Everything loads perfectly on the page, the elements are created for me, but I can’t grab them from another function connected to the page. Here’s examples of codes. I have two external function, combined in one js file and simple html page
This is fillCatalog.js (First file)
const row = document.querySelector('.catalog__row');
export const fill = function (brand) {
fetch(`./data/${brand}.json`)
.then(function (response) {
return response.json();
})
.then(function (data) {
let products = [...data.products];
products.forEach(product => {
row.insertAdjacentHTML(
'afterbegin',
` <a class="catalog__link" href="#" >
<div class="catalog__product">
<div class="catalog__product-img">
<img class="catalog__productImg" src=${product['img-src']} alt="" srcset="" />
</div>
<h3 class="catalog__product-model">${product['model']}</h3>
<p class="catalog__product-brand">${product['brand']}</p>
<span class="catalog__product-price">${product['price']}</span>
</div></a>`
);
});
});
};
This is productSave.js (Second file)
class Product {
constructor(cardImg, cardName, cardBrand = '', cardPrice) {
this.cardImg = cardImg;
this.cardName = cardName;
this.cardBrand = cardBrand;
this.cardPrice = cardPrice;
}
}
const links = document.querySelectorAll('.catalog__link');
export const productSave = function () {
window.addEventListener('DOMContentLoaded', () => {
console.log(links);
links.forEach(link => {
link.addEventListener('click', e => {
productItem = link.querySelector('.catalog__product');
const newProduct = new Product(
productItem.querySelector('catalog__productImg').src,
productItem.querySelector('.catalog__product-model').textContent,
productItem.querySelector('.catalog__product-brand').textContent,
productItem
.querySelector('.catalog__product-price-price')
.textContent.replace(/\D/g, '')
);
localStorage.setItem('newCard', JSON.stringify(newProduct));
console.log(card.querySelector('.card__name').textContent);
});
});
});
};
This is third file where I invoke external function
loadContent.js
import { fill } from './fillCatalog.js';
import { productSave } from './productSave.js';
fill('jordans');
productSave();
Simple HTML
<html lang="ru">
<head>
<!--=include head.html-->
</style>
</head>
<body>
<div class="catalog">
<div class="catalog__content">
<div class="catalog__row"></div>
</div>
</div>
</body>
<script type="module" src="../js/goodscart.js"></script>
<script type="module" src="../js/loadContent.js"></script>
</html>
I've tried use beforeend afterent and etc. Tried also use getElementsByTag, it returns empty HtmlCollection[]. After insertAdjacentHTML, can't select like usually this links.
Could anyone help me please with this issue? I can't find solution for that. Thank you
You need to define links
inside the function to avoid it being initialized before the elements are rendered.
links
class Product {
...
}
// since this is not in a function it is initialized on import so
// before you render the `catalog__link`
const links = document.querySelectorAll('.catalog__link'); // <-- This
export const productSave = function () {
// <-- Should be here
...
};
fill
PromiseFuthermore there is a fetch reuqest inside fill
which returned a promise.
Then you can properly chain the call to the following function like so
// productSave will only be executed after the
// fetch from fill has concluded sucesscully
fill('jordans').then(productSave);
export const fill = function (brand) {
return fetch(`./data/${brand}.json`).then(...)
// return as a promise
};
You can also implement a detector that waits for at least one link to be rendered. This Promise
would resolve after at least 1
item with catalog__link
can be found. Then you can chain your logic with .then(...)
. This one also includes a timeout of 10 seconds which executes .catch(...)
(if present otherwise throws) if the element was not found in time.
new Promise((res, rej) => {
const timeout = setTimeout(() => {
clearTimeout(timeout);
clearInterval(interval);
rej("Not Found")
}, 10000);
const interval = setInterval(() => {
const links = document.querySelectorAll('.catalog__link');
if (links.length == 0) {
return;
}
clearInterval(interval);
clearTimeout(timeout);
res(links)
},200);
});