- I have trouble grabbing dynamically created DOM elements and
- I don't know where the subsequent click event delegation should take place (i.e. in my main .js file or inside the UI class).
This object-oriented learning project sends async HTTP requests using fetch to the Punk API and fetches a list of beers based on user search query. The output is not hard-coded but the innerHTML of the list is dynamically created from the API data inside a class called UI.
In app.js I handle the promises and use ui.paint() to display the beers on the page.
Each beer has a 'buy' button that is also dynamically created.
I want to grab all the buttons, loop through them, attach a click event.
I've tried:
grabbing the buttons in app.js with queryselectorAll - it doesn't work, it returns an empty node list - this selector must have been hoisted and at the time of calling the buttons clearly don't exist yet - IS THERE SOME ASYNC WAY to solve this and wait for the API data to come back and for the new elements to be created? (note: once I have the list of beers, I can grab the full node list with querySelectorAll in the browser console...)
I also tried wrapping the queryselectorAll inside a function and call that function inside the click event function that handles the promises. It did not work either.
I tried putting the event in app.js as well as inside the UI prototype class (which looked neater) but none of them works.
app JS
const ui = new UI();
document.getElementById('submit-button- name').addEventListener('click', (e) => {
e.preventDefault();
const newBeer = new Beer();
newBeer.getByName()
.then(beerResult => {
ui.paint(beerResult);
})
.catch(err => {
console.log(err)
});
});
const buyBtns = document.querySelectorAll('#buy-btn');
buyBtns.forEach((buyBtn) => {
buyBtn.addEventListener('click', (e) => {
ui.addToCart(e)
});
});
then here is the UI class, trying to pick up the event (I'm trying to grab the button that's in the last <a> tag of the innerHTML and push it in a cart array which I would later display on a separate page).
class UI {
constructor() {
this.productRow = document.getElementById('products-row');
}
paint(beerResult) {
let output = '';
beerResult.forEach(beer => {
output +=
`<div class="card">
<img id="beer-card-img" src="${beer.image_url}" alt="beer-image" class="card-image-top">
<div class="card-body">
<h5 class="card-title">${beer.name}</h5>
<h6 class="tagline">${beer.tagline}.</h6>
<p id="description" class="card-text">${beer.description}</p>
<p id="abv" class="card-text">ABV: ${beer.abv}</p>
<p id="ibu" class="card-text">IBU: ${beer.ibu}</p>
<p id="ph" class="card-text">PH: ${beer.ph}</p>
<p id="first-brewed" class="card-text">First brewed: ${beer.first_brewed}</p>
<a href="#" class="btn btn-outline-dark btn-sm">See details</a>
<a href="#" id="buy-btn" class="btn btn-danger btn-sm">Buy</a>
</div>
</div>`
});
// console.log(output);
this.productRow.innerHTML = output;
}
addToCart(e) {
let cart = [];
if (e.target.parentElement.parentElement.classList.contains('card')) {
cart.push(e.target.parentElement.parentElement)
}
console.log(cart);
}
}
Right now, nothing happens when I click 'Buy'. What I want to happen is beers being pushed into an array (as objects) to be later displayed on a separate page.
Thanks of your patience, time and advice!