I need your help. I have an array of objects and an empty array that I need to add elements to. I am trying to output my finished items and the items I have in the cart. The fact is that I do not have the elements of the cart array displayed, although the elements are added to it. How can you update an array when elements are added to it and output them? Thank you very much
for (let element of arr) {
let paragraph = document.createElement('p');
paragraph.innerText = `${element.id} ${element.name} ${element.status}`;
let button = document.createElement('button');
button.innerText = 'Add to list';
button.addEventListener('click', () => {
if (!cart.includes(element)) {
cart.push(element);
}
console.log(cart);
})
paragraph.appendChild(button);
document.body.appendChild(paragraph);
}
for (let element of cart) {
let p = document.createElement('p');
p.innerText = `${element.id} ${element.name} ${element.status}`;
document.body.appendChild(p);
console.log(p);
}
CodePudding user response:
If we go through your current code step-by-step, we can see that (simply put):
- For every element in
arr
, a button is created that pushes something tocart
. - Then (immediately), for every element in
cart
, a paragraph is created.
After creating the paragraphs, cart
may change again. But JavaScript is not a declarative language, so your for (let element of cart)
loop won't react to future changes. Instead it is executed only once, when the program flow reaches that point.
That means you need to make the program flow reach the relevant code (creating a paragraph) again for every time it needs to run, i.e. every time something is pushed to cart
.
And cart
changes every time in the if-statement of the button's listener, so let's create a paragraph then:
const arr = [
{ id: 0, name: "Banana", status: "available" },
{ id: 1, name: "Apple", status: "sold out" },
];
const cart = [
{ id: 2, name: "Tomato", status: "fresh" }
];
for (let element of arr) {
const p = document.createElement('p');
p.innerText = `${element.id} ${element.name} ${element.status}`;
const button = document.createElement('button');
button.innerText = 'Add to list';
button.addEventListener('click', () => {
if (!cart.includes(element)) {
cart.push(element);
createParagraphFrom(element); // Add if not included yet
}
console.log("Cart:", cart);
});
p.appendChild(button);
document.body.appendChild(p);
}
for (let element of cart) {
createParagraphFrom(element); // Add initial cart contents
}
// Adding initial and eventual content works the same.
// This meaning we can use a function for this.
function createParagraphFrom(obj) {
const p = document.createElement('p');
p.innerText = `${obj.id} ${obj.name} ${obj.status}`;
document.body.appendChild(p);
console.log("Added paragraph:", p);
}
HTML elements can have custom data attributes that we can utilize. (Note that their values will always be of type string.)
When also using an element's children
property, we won't need an extra backing array. That reduces redundant data and removes a point of confusion (which was your initial problem):
const list = document.getElementById("list");
const cart = document.getElementById("cart");
const listData = [
{ id: 0, name: "Banana", status: "freshly imported" },
{ id: 1, name: "Tomato", status: "just arrived" }
];
for (let element of listData) {
addDataButton(element);
}
// ... (leaving out creation of initial cart content)
function addCartElement(data) {
const element = document.createElement("li");
// Copy relevant data (i.e. id) to P
element.dataset.id = data.id;
// Display (any) user-friendly text
element.textContent = `${data.name}, ${data.status}`;
cart.append(element);
}
function addDataButton(el) {
const element = document.createElement("li");
// Save data on element
element.dataset.id = el.id;
element.dataset.name = el.name;
element.dataset.status = el.status;
// Display (any) user-friendly text
element.textContent = `${el.name}, ${el.status}`;
const button = document.createElement("button");
button.textContent = "Add to cart";
button.addEventListener("click", () => {
// Check if element with same ID exists in cart
let existsInCart = false;
for (let child of cart.children) {
if (child.dataset.id === element.dataset.id) {
// If child has same ID, then item exists in cart
existsInCart = true;
break;
}
}
if (!existsInCart) {
addCartElement(element.dataset);
}
});
element.append(button);
list.append(element);
}
<p>List</p>
<ul id="list"></ul>
<p>Cart</p>
<ul id="cart"></ul>