I have been trying to get this solved for a while, but can't figure it out.
I received some products from an API and after deconstructing, I passed them into some HTML tags to perform DOM manipulation on them from JS like so...
displayProducts(products){
let result = '';
products.forEach(product => {
result += `
<article class="product">
<div class="img-container">
<img src=${product.image} alt="product" class="product-img">
<button class="bag-btn" data-id= ${product.id}>
<img src="./images/icons8_add_shopping_cart_100px.png"
width="16px" max-width= "18px" alt="add to cart"/>
Add to cart
</button>
</div>
<div class="goodsinfo">
<span class="description"> <img src="./images/icons8_eye_100px.png" class="view" data-class=${product.id}/>
</span>
<span class="titleprice">
<h3>${product.title}</h3>
<h4>#${product.price}</h4>
</span>
</div>
</article>
`
});
productsDOM.innerHTML = result;
}
Then I started manipulating, but I ran into a problem using the .find array method to match an id with the id from the array of objects even though they're exactly the same.
compileDescription(products){
console.log(products) // this works fine and displays 16 products which are 16 objects in arrays.
const eyeView = [...document.querySelectorAll('.view')];
eyeView.forEach(viewBtn=>{
viewBtn.addEventListener('click', (event)=>{
const btnId = event.target.dataset.class
console.log(btnId); //this works and shows the ID of the clicked button
const productFind = products.find(product=> product.id === btnId)
console.log(productFind); //returns undefined
})
})
}
What I want is that when a button with a particular id is clicked, the id is matched with an id of an object in the array of objects and the object is returned to me for manipulation.
Please help me guys. Thanks ahead.
I'm currently trying to display an individual.html page based off what the user clicks on a products.html page. For example, clicking on product1 would show the individual page for product1 and clicking on product2 would show the individual page for product2 etc...
I currently have a UI class in my app.js file which holds the logic for displaying information.
There is a function in there called displayIndividualProject() which has an event listener that says, "If a card is clicked (if (event.target.classList.contains('img-container'))), then inject this HTML structure into the individual.html" page. However, after I click on it, it's not injecting the sequence.
Also, displayIndividualProduct is meant for the individual.html page while displayProducts is meant for the product.html page.
Here is what part of my UI class looks like:
const individualProductsDOM = document.querySelector('.single-product');
class UI{
displayIndividualProduct(){
document.addEventListener("click", event => {
if (event.target.classList.contains('img-container'))
individualProductsDOM.innerHTML =
`
<div class='section-title'>
<h2>${product.title}</h2>
</div>
<div class='indi-img-container'>
<img src=${product.image} data-id='${product.id} alt="">
</div>
<div class="product-footer">
<h3>Estimated Cost: $ <span class='item-total'>0</span></h3>
<button class='bag-btn-2' data-id='${product.id}'>
<i class='fas fa-shopping-cart'></i>
add to cart
</button>
</div>
`
})
individualProductsDOM.innerHTML += `injected`;
}
displayProducts(products){
let result = '';
products.forEach(product => {
result += `
<article class="product" data-id='${product.id}'>
<div class='img-container'>
<a href='/individual.html'>
<img src=${product.image} alt="product" class='product-img' data-id='${product.id}'>
<button class='bag-btn' data-id='${product.id}'>
<i class='fas fa-shopping-cart'></i>
add to cart
</button>
</a>
</div>
<h3>${product.title}</h3>
<h4>$${product.price}/roll</h4>
</article>
`
});
//insert the products into the productsDOM
productsDOM.innerHTML = result;
}
}
and this is being called at the bottom of my JS through ui.displayIndividualProduct();
document.addEventListener("DOMContentLoaded", () => {
const ui = new UI();
const products = new Products();
// setup app
ui.setupAPP();
//get all products
products.getProducts().then(products => {
//first display, then save, and then connect the add cart buttons
ui.displayProducts(products);
Storage.saveProducts(products);
}).then( () => {
ui.getBagButtons();
});
ui.displayIndividualProduct();
});
any help would be appreciated!
I figured out how to change the icon when more elements are added, but I can't figure out to how return it to the original icon while the cart is empty.
Javascript
let cartItems = document.getElementsByClassName('cart-container')[0];
if (cartItems.childElementCount <= 1) {
let cartBtn = document.getElementsByClassName('cart-btn')[0]
cartBtn.innerHTML = `
<i class="fa fa-cart-plus cart-btn text-danger"></i>`
}
HTML
<h1 class="cart-btn">
<i class="fa fa-shopping-cart"></i>
</h1>
<div class="container cart-container d-flex flex-column pb-5">
<div class="row mt-5 mb-4">
<div class="col">
</div>
</div>
</div>
I have a shopping cart button on a navbar that I need to switch between different states depending on if the cart is empty or not. I figured out how to change it to one state when an item was added to the cart, but couldn't figure out how to change it back to the original state when I emptied the cart. However, I used this code to accomplish that task.
Javascript
function checkNavBtn() {
let cartItems = document.getElementsByClassName('cart-container')[0];
let cartBtn = document.getElementsByClassName('fa-shopping-cart')[0];
if (cartItems.childElementCount >= 0) {
cartBtn.classList.add('fa-cart-plus', 'text-danger');
} if (cartItems.childElementCount <= 0) {
cartBtn.classList.remove('fa-cart-plus', 'text-danger');
}
}
Trying to make the "close-circle" button work. this function will create the button multiple times if there are multiple items, each of them is created with a specific id, and once it is clicked, will delete all the data based on that id. Somehow ONLY the first created button works, the rest just do nothing. I believe there is something wrong with the way of creating the addEventListener.
if (inCartItem) {
productsContainer.innerHTML = '';
// item means each item in the object which is the inCartItem
Object.values(inCartItem).map(item => {
productsContainer.innerHTML += `
<div class ="product-incart">
<ion-icon id ="${item.Id}CloseBtm" name="close-circle"></ion-icon>
<img src="${item.src}">
<span>${item.Productname}</span>
</div>
<div class="price-incart">$${item.Price}</div>
<div class="quantity">
<ion-icon name="caret-back-outline"></ion-icon>
<span>${item.inCart}</span>
<ion-icon name="caret-forward-outline"></ion-icon>
</div>
<div class="total">
$${item.inCart * item.Price},00
</div>
`
console.log(item.Id);
document.getElementById(item.Id + "CloseBtm").addEventListener("click", ("click", () => { removeAll(item); }));
})
So I'm currently doing a Calorie Counter project that consists on giving the user the option to firstly, add items with the respective name and number of calories, remove items or update them when clicking on an edit icon next to the item, and finally removing all items at once.
The UI will basically display all the items that the user has added (including the name and the number of calories), where each item will have an edit icon next to it, and if the icon is clicked, it will give the user the option to edit them and delete them.
I still haven't gotten to the edit part because I'm currently stuck in the delete part.
Let's say I have 3 items in the list, when I click on the edit button and then delete, everything works out fine, the html element is deleted and it looks good. If I repeat the process one more time it still works, but when I repeat the process one last time, the problem happens.
For some reason, when I hit the edit button nothing happens, I've checked and apparently the item array is completely empty, even though I only deleted 2 out of the 3 items.
I've tried everything and I've been completely stuck for 3 days straight.
// Item Controller
const ItemController = function() {
// Hard coded items
data = [{
name: "Hamburguer",
id: 0,
calories: 1000
},
{
name: "Pasta",
id: 1,
calories: 700
},
{
name: "Apple",
id: 2,
calories: 70
}
]
return {
getItems: function() {
return data;
},
deleteAllItems: function() {
data.items = [];
UIController().clearItems();
},
getTotalCalories: function() {
totalCalories = 0;
this.getItems().forEach(item => {
totalCalories += parseInt(item.calories)
});
UIController().changeToTotalCalories(totalCalories);
},
removeSingleItem: function(item, li) {
// Getting the index of the item
indexItem = items.getItems().indexOf(item);
// Deleting item from array
items.getItems().splice(indexItem, 1);
// Deleting li item from UI
li.remove();
console.log(items.getItems());
}
}
};
const items = ItemController();
// UI controller
const UIController = function() {
return {
displayItems: function(itemsPresented) {
itemsPresented.forEach(function(item) {
itemList = document.getElementById("item-list");
itemList.innerHTML += `
<li class="collection-item" id="${item.id}">
<strong>${item.name}: </strong><em>${item.calories} calories</em>
<a href="#" class="secondary-content">
<i class="edit-item fa fa-pencil">
</i>
</a>
</li>
`;
})
},
clearItems: function() {
itemList = document.getElementById("item-list");
itemList.innerHTML = "";
items.getTotalCalories();
},
changeToTotalCalories: function(totalCalories) {
document.querySelector(".total-calories").textContent = totalCalories;
},
}
}
const uiCtrl = UIController();
// So when the page loads, the hard coded items can be represented
uiCtrl.displayItems(items.getItems());
// To delete all the items at once
clearAllBtn = document.querySelector(".clear-btn");
clearAllBtn.addEventListener("click", (e) => {
items.deleteItems();
e.preventDefault();
})
// Getting the li element (The one that has all the hard-coded items)
itemList = document.getElementById("item-list");
itemList.addEventListener("click", e => {
// Checking if the user is clicking the Edit Icon
if (e.target.classList.contains("edit-item")) {
items.getItems().forEach(item => {
li = e.target.parentElement.parentElement;
// Getting the item that has the edit icon that the user clicked
if (item.id === parseInt(e.target.parentElement.parentElement.id)) {
// Putting the name and the calories of the item that is being edited in the input fields
document.getElementById("item-name").value = item.name;
document.getElementById("item-calories").value = item.calories;
// Changing the buttons so when the user edits an item, they have the options Update and Delete
document.querySelector(".add-btn").style.display = "none";
document.querySelector(".update-btn").style.display = "block";
document.querySelector(".delete-btn").style.display = "block";
document.querySelector(".back-btn").style.display = "none";
// If the user clicks the delete button
document.querySelector(".delete-btn").addEventListener("click", e => {
// Changing all the buttons back to normal
document.querySelector(".add-btn").style.display = "block";
document.querySelector(".update-btn").style.display = "none";
document.querySelector(".delete-btn").style.display = "none";
document.querySelector(".back-btn").style.display = "block";
// Clearing out the input fields
document.getElementById("item-name").value = "";
document.getElementById("item-calories").value = "";
// Deleting item
items.removeSingleItem(item, li);
// Updating the calories
items.getTotalCalories();
e.preventDefault();
});
}
});
}
})
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<nav>
<div class="nav-wrapper blue">
<div class="container">
<a href="#" class="brand-logo center">
Tracalorie
</a>
<ul class="right">
<li>
<a href="#" class="clear-btn btn blue lighten-3">
Clear All
</a>
</li>
</ul>
</div>
</div>
</nav>
<br>
<div class="container">
<!-- Form Card -->
<div class="card">
<div class="card-content">
<span class="card-title">
Add Meal / Food Item
</span>
<form class="col">
<div class="row">
<div class="input-field col s6">
<input type="text" id="item-name" placeholder="Add item">
<label for="item-name">Meal</label>
</div>
<div class="input-field col s6">
<input type="text" id="item-calories" placeholder="Add calories">
<label for="item-calories">Calories</label>
</div>
<button class="add-btn btn blue darken-3"><i class="fa fa-plus"></i>
Add Meal</button>
<button style="display: none;" class="update-btn btn orange" display=><i class="fa fa-pencil-square-o"></i>
Update Meal</button>
<button style="display: none;" class="delete-btn btn red"><i class="fa fa-remove"></i>
Delete Meal</button>
<button class="back-btn btn grey pull-right"><i class="fa fa-chevron-circle-left"></i>
Back</button>
</div>
</form>
</div>
</div>
<!-- Calorie Count -->
<h3 class="center-align">Total Calories: <span class="total-calories">
0
</span></h3>
<!-- Item list -->
<ul id="item-list" class="collection">
</ul>
</div>
It seems like you add an eventListener to the delete button every single time a user clicks on the edit pencil. You never remove these eventListeners. So when the first edit is done, there is one delete event and one items gets deleted. The next time a user clicks on the edit button, a second event gets added to the same html element, thus two items gets deleted (both events will trigger one after the other). This becomes apparent when your hardcoded list would contain 10 items, you would see 1,2,3 and lastly 4 items disappear. I suggest you look into resetting/removing eventlisteners.