I am doing an e-commerce project. I am currently trying to create the functionality of the quantity amount for each new added item. However, when the amount of one item altered, though it does not change the other item's quantity, when you try to alter the other item's quantity it starts from the number of the previous item's quantity.
For example, if I make the quantity of 'item1' = 3. Then if I adjust 'item2', whether I increase or decrease, it starts from 3, instead of 1.
I think I am maybe complicating it for myself I am still new to JavaScript.
const quantityIncDec = function (plus, minus) {
let quantity = 1;
plus.forEach(button => {
button.addEventListener('click', function () {
quantity++;
this.parentElement.children[1].textContent = quantity;
});
});
minus.forEach(button => {
button.addEventListener('click', function () {
if (quantity > 1) {
quantity--;
this.parentElement.children[1].textContent = quantity;
}
});
});
};
// Add to Cart Preview
btnAddCart.forEach(element => {
element.addEventListener('click', function () {
const markup = `
<li class="index-preview-list-item">
<img src="${this.parentElement.children[0].src}" alt="" />
<div>
<h4 class="product-name">${this.parentElement.children[1].textContent}</h4>
<div class="quantity">
<button class="btn btn-plus">
<i class="fa-solid fa-plus"></i>
</button>
<p class="quantity-value">1</p>
<button class="btn btn-minus">
<i class="fa-solid fa-minus"></i>
</button>
</div>
</div>
<button class="btn btn-delete">
<i class="fa-solid fa-trash-can"></i>
</button>
</li>
`;
clearPreviewText(previewTextCart);
cartPreviewContainer.insertAdjacentHTML('beforeend', markup);
const btnPlus = document.querySelectorAll('.btn-plus');
const btnMinus = document.querySelectorAll('.btn-minus');
quantityIncDec(btnPlus, btnMinus);
const btnDelete = document.querySelectorAll('.btn-delete');
deleteItem(btnDelete);
});
});
The let keyword is used to create a block-scoped variable. So, you will have a single instance of that variable in its block. Let's illustrate this in two examples:
let quantity = 0;
for (let button of document.querySelectorAll(".btn")) {
button.addEventListener("click", function() {
alert(++quantity);
});
}
<input class="btn" type="button" value="1">
<input class="btn" type="button" value="2">
<input class="btn" type="button" value="3">
<input class="btn" type="button" value="4">
<input class="btn" type="button" value="5">
<input class="btn" type="button" value="6">
<input class="btn" type="button" value="7">
As you can see, the counter is being updated whenever you click on a button and it does not separately keep track of each button's quantity. This is your mistake. Now, let's see a corrected example, where quantity is created in the correct block:
for (let button of document.querySelectorAll(".btn")) {
let quantity = 0;
button.addEventListener("click", function() {
alert(++quantity);
});
}
<input class="btn" type="button" value="1">
<input class="btn" type="button" value="2">
<input class="btn" type="button" value="3">
<input class="btn" type="button" value="4">
<input class="btn" type="button" value="5">
<input class="btn" type="button" value="6">
<input class="btn" type="button" value="7">
If you click on different buttons here, then you will see that each is having "its own" quantity. So, having said this, let's apply a similar fix for your code:
const quantityIncDec = function (plus, minus) {
//quantity is no longer defined here, because then it's out of the loop's
//block and its value will be shared between the buttons
plus.forEach(button => {
//Instead, we create quantity here and it will be therefore in the
//context of the "current" button
let quantity = 1;
button.addEventListener('click', function () {
quantity++;
this.parentElement.children[1].textContent = quantity;
});
});
minus.forEach(button => {
button.addEventListener('click', function () {
if (quantity > 1) {
quantity--;
this.parentElement.children[1].textContent = quantity;
}
});
});
};
// Add to Cart Preview
btnAddCart.forEach(element => {
element.addEventListener('click', function () {
const markup = `
<li class="index-preview-list-item">
<img src="${this.parentElement.children[0].src}" alt="" />
<div>
<h4 class="product-name">${this.parentElement.children[1].textContent}</h4>
<div class="quantity">
<button class="btn btn-plus">
<i class="fa-solid fa-plus"></i>
</button>
<p class="quantity-value">1</p>
<button class="btn btn-minus">
<i class="fa-solid fa-minus"></i>
</button>
</div>
</div>
<button class="btn btn-delete">
<i class="fa-solid fa-trash-can"></i>
</button>
</li>
`;
clearPreviewText(previewTextCart);
cartPreviewContainer.insertAdjacentHTML('beforeend', markup);
const btnPlus = document.querySelectorAll('.btn-plus');
const btnMinus = document.querySelectorAll('.btn-minus');
quantityIncDec(btnPlus, btnMinus);
const btnDelete = document.querySelectorAll('.btn-delete');
deleteItem(btnDelete);
});
});
EDIT
The solution above was ignoring that a minus button also has to work with the same quantity, so I apply a fix for it:
const quantityIncDec = function (plus, minus) {
//quantity is no longer defined here, because then it's out of the loop's
//block and its value will be shared between the buttons
let limit = Math.max(plus.length, minus.length);
for (index = 0; index < limit; index++) {
if (plus.length > index) {
let plusButton = plus[index];
plusButton.addEventListener('click', function () {
quantity++;
this.parentElement.children[1].textContent = quantity;
});
}
if (minus.length > index) {
let minusButton = minus[index];
minusButton.addEventListener('click', function () {
if (quantity > 1) {
quantity--;
this.parentElement.children[1].textContent = quantity;
}
});
}
}
};
// Add to Cart Preview
btnAddCart.forEach(element => {
element.addEventListener('click', function () {
const markup = `
<li class="index-preview-list-item">
<img src="${this.parentElement.children[0].src}" alt="" />
<div>
<h4 class="product-name">${this.parentElement.children[1].textContent}</h4>
<div class="quantity">
<button class="btn btn-plus">
<i class="fa-solid fa-plus"></i>
</button>
<p class="quantity-value">1</p>
<button class="btn btn-minus">
<i class="fa-solid fa-minus"></i>
</button>
</div>
</div>
<button class="btn btn-delete">
<i class="fa-solid fa-trash-can"></i>
</button>
</li>
`;
clearPreviewText(previewTextCart);
cartPreviewContainer.insertAdjacentHTML('beforeend', markup);
const btnPlus = document.querySelectorAll('.btn-plus');
const btnMinus = document.querySelectorAll('.btn-minus');
quantityIncDec(btnPlus, btnMinus);
const btnDelete = document.querySelectorAll('.btn-delete');
deleteItem(btnDelete);
});
});
I'm not sure, If I understood your question correctly. The problem in your source code is, that the only single quantity variable you are using for all of your products. But each product has it's own quantity state, therefore In your case, If you will modify quantity of one product, you will also modify initial quantity state of all another products.
Inside listener callback method, you must firstly get the previous quantity state for the current product and after that increment that value. Also another way is for example save current product quantities into array variable, and then you will need firstly find the initial quantity state from right array value, then increment and then save back modified value to that array.
// ...or ungly but most simple solution, take text content, convert to Int, then increment and store back to the element
this.parentElement.children[1].textContent = parseInt(this.parentElement.children[1].textContent)+1;
Edited answer:
// Write Javascript code!
const appDiv = document.getElementById('app');
appDiv.innerHTML = `<div id="basket">Basket</div>`;
let items = [
{
id: 24,
name: 'halabala',
qty: 10,
},
];
function Basket() {
const render = () => {
const elBasket = document.getElementById('basket');
elBasket.innerHTML = ''; // Remove all childs from previous render
// console.log(elBasket);
if (items.length) {
items.forEach((item) => {
const that = this;
const elDiv = document.createElement('div');
const elSpan = document.createElement('span');
elSpan.innerText = ' ' + item.name + ' (' + item.qty + ')';
const btnPlus = document.createElement('button');
btnPlus.innerText = '+';
btnPlus.addEventListener('click', (event) => {
event.preventDefault();
Basket().incrementItem(item.id);
});
const btnMinus = document.createElement('button');
btnMinus.innerText = '-';
btnMinus.addEventListener('click', (event) => {
event.preventDefault();
Basket().decrementItem(item.id);
});
elDiv.appendChild(btnPlus);
elDiv.appendChild(btnMinus);
elDiv.appendChild(elSpan);
elBasket.appendChild(elDiv);
});
}
};
return {
init: () => {
render();
},
addItem: (itemId, itemName, gty = 1) => {
items.push({ id: itemId, name: itemName, qty: qty });
render();
},
removeItem: (itemId) => {
items = items.filter((i) => i.id != itemId);
render();
},
incrementItem: (itemId) => {
const item = items.find((i) => i.id === itemId);
if (!item)
throw new Error('Unable to increment, because item not found!');
item.qty++;
render();
},
decrementItem: (itemId) => {
const item = items.find((i) => i.id === itemId);
if (!item)
throw new Error('Unable to decrement, because item not found!');
item.qty--;
render();
},
clearBasket: () => {
items = [];
},
};
}
Basket().init();
Related
I am trying to make a logic that if user input, date, and time are not filled it gives the user an alert to have the mentioned sections above, but for some reason, even when I have those sections containing values it still shows null.
The outcome should be:
If there is no value for user input, a due date or time alert should appear.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask(taskNew, taskDate, taskTime) {
if (taskNew == null) {
alert("Please add a new Task")
}
if (taskDate == null) {
alert("Please add a new Task with a due date");
}
if (taskTime == null) {
alert("Please add a new Task with a due time");
}
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
All values obtained from form elements are strings. Even if the element is empty, the value is still "", not null so checking for null isn't the right approach.
Instead test for the absence of any "truthy" value as seen below.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask(taskNew, taskDate, taskTime) {
if (!taskNew) {
alert("Please add a new Task")
}
if (!taskDate) {
alert("Please add a new Task with a due date");
}
if (!taskTime) {
alert("Please add a new Task with a due time");
}
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
Thanks for the help.
I also found another way of doing it as well after doing further research.
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time")
};
//local storage key
const storage_key = "tasks-storage-key";
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = [];
// function that renders task list
//function that creates new tasks with date and time
const creatTask = (task) => {
const id = createId();
const taskNew = el.input.value;
const taskDate = el.date.value;
const taskTime = el.time.value;
if (taskNew.length == 0) {
alert("Please add a new Task");
}
if (taskDate.length == 0) {
alert("Please add a new Task with a due date");
}
if (taskTime.length == 0) {
alert("Please add a new Task with a due time");
}
const tasks = document.createElement("div");
tasks.innerHTML = `
<div class="task-content">
<div class="list-of-task">
<div class="task" data-id="${id}">
<input type="checkbox" class="tick">
<div class="new-task-created">${taskNew}</div>
<label class="due-date">${taskDate}</label>
<label class="due-time">${taskTime}</label>
</div>
<div class="atcion-buttons">
<button class="edit" data-id="">Edit</button>
<button class="delete" data-id="">Delete</button>
</div>
</div>`;
taskList.push(tasks);
console.log(taskList);
el.list.appendChild(tasks);
return task
};
//event listner that listens for add button.
function addTask() {
creatTask();
}
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
I am making a calculator, and creating a new element (the number) every time I press an operator button. I'm wondering how I can get it to where I can infinitely add numbers (1+1+1+1 etc) every time i create the element. I think I'll need to store the current total number somehow, but I can't figure out how to do it. If you need any clarification let me know! Thanks
HTML
<button class="numbers">0</button>
<button class="numbers">1</button>
<button class="numbers">2</button>
<button class="numbers">3</button>
<button class="numbers">4</button>
<button class="numbers">5</button>
<button class="numbers">6</button>
<button class="numbers">7</button>
<button class="numbers">8</button>
<button class="numbers">9</button>
<button id="addition" class="button-operator">+</button>
<button id="equals" class="button-operator">=</button>
<div class="result-container">
<h1 id="value"></h1>
<h1 id="equal"></h1>
</div>
JS
const numbersArray = Array.from(document.querySelectorAll('.numbers'));
const addition = document.getElementById('addition');
const equal = document.getElementById('equal');
const equalSign = document.getElementById('equals');
let newValue = document.createElement('h1');
numbersArray.forEach(element => {
element.addEventListener('click', (e) => {
newValue.innerText = e.target.innerText;
function resultHandler() {
value.innerText = ''
}
addition.addEventListener('click', (e) => {
resultHandler()
})
equalSign.addEventListener('click', (e) => {
equal.innerText = parseInt(newValue.innerText) + parseInt(newValue.innerText);
value.style.display = 'none';
newValue.style.display = 'none';
operator.style.display = 'none';
})
})
})
I'm a beginner and I wanted to start building projects and I want to determine the number of rounds using the value of the button the user clicks on.
`<div class="game-type">
<h2>GAME TYPE</h2>
<p>choose the number of rounds you'd like to play</p>
<div class="options">
<button value="1" type="button">BEST OF 1</button>
<button value="3" type="button">BEST OF 3</button>
<button value="5" type="button">BEST OF 5</button>
<button value="1000" type="button">FREE MODE</button>
</div>
</div>
`
using JavaScript I tried retrieving the value with "this" for the button that is clicked but it doesn't return anything at all. I want to use the value to set the RoundLimit value equal to the button pressed and end the game when RoundCount is equal to the round limit.
`const rpsGame = () => {
let playerScore = 0;
let computerScore = 0;
let roundCount = 0;
let roundLimit = 0;
// Universal value
const gameType = document.querySelector(".game-type");
//pick number of rounds to play
const roundNum = () => {
const roundSelector = document.querySelectorAll(".game-type buttons");
roundSelector.forEach(roundSelector => {
roundSelector.addEventListener("click", function () {
console.log(this);
});
});
};
roundNum();`
Im a beginner any criticism is appreciated. Thank you!
it's button instead of buttons
const roundNum = () => {
const roundSelector = document.querySelectorAll(".game-type button");
roundSelector.forEach(roundSelector => {
roundSelector.addEventListener("click", function () {
console.log(this);
});
});
};
roundNum();
and to get the value
const roundNum = () => {
const roundSelector = document.querySelectorAll(".game-type button");
roundSelector.forEach(roundSelector => {
roundSelector.addEventListener("click", function (e) {
console.log(e.target.value);
});
});
};
roundNum();
Here is my buttons with different values.
<div id="RAM_BtnGroup" class="btn-group" role="group" aria-label="Basic example" name='ram'>
<button type="button" value="8GB" class="btn btn-outline-success">8GB </button>
<button type="button" value="16GB" class="btn btn-outline-success">16GB </button>
<button type="button" value="32GB" class="btn btn-outline-success">32GB </button>
<button type="button" value="64GB" class="btn btn-outline-success">64GB </button>
Price: <span id="totalCost"></span>
So when I randomly click on different buttons, I'm still getting back the value of 8GB.
var ram = document.querySelector("button[type=button]");
ram.addEventListener('click', calculateTotal)
So how should I click on different buttons in order to get different values?
Should I get the value of button regards to something like this? But it is not working in this way.
var ram = document.querySelector("button[value=8GB][value=16GB][value=32GB][value=64GB]");
million thanks to the suggested solution, it's good enough but my calculateTotal function still not counting on the ram.Can you guide me how to fix this out. Here is my code. I am pretty sure that unitcost, additional and qty run smoothly, but after added in ramcost to get the value of ram, it seem to not returning any total price of all items.
var ram = document.querySelectorAll('button[type="button"]');
ram.forEach((ramm) => {
ramm.addEventListener('click', calculateTotal)
});
var ram_price = {};
ram_price['8GB'] = 200;
ram_price['16GB'] = 300;
ram_price['32GB'] = 400;
ram_price['64GB'] = 500;
function calculateTotal
() {
var ramcost = ram_price[ramm.value];
var unitCost = product_price[productEl.value];
var additionalCost = size_price[sizeEl.value] || 0;
var qty = quantityEl.value || 0;
totalCostEl.textContent = `Total cost: $${(unitCost + additionalCost + ramcost) * qty}`;
}
Can you please check this difference,
queryselector_class:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_document_queryselector_class
as well as
queryselectorall_class:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_document_queryselectorall_class
I think it will help.
You have to attach the click event to every button. querySelector() returns only the first matching element, while querySelectorAll() returns all matching elements. That's why you need to use the latter:
var ram = document.querySelectorAll("button[type=button]");
ram.forEach((r) => {
r.addEventListener('click', calculateTotal)
});
function calculateTotal(e) {
console.log(e.target.value);
}
<button type="button" value="8GB" class="btn btn-outline-success">8GB </button>
<button type="button" value="16GB" class="btn btn-outline-success">16GB </button>
<button type="button" value="32GB" class="btn btn-outline-success">32GB </button>
<button type="button" value="64GB" class="btn btn-outline-success">64GB </button>
<div id='container'></div>
I hope this helps, you can create a class ButtonToPress and then instance buttons. Its working
class ButtonToPress {
constructor(n) {
this.n = n;
const button = document.createElement('button');
button.onclick = this.print.bind(this);
button.innerText = this.n
document.getElementById('container').appendChild(button);
}
print() {
console.log(this.n);
}
}
let buttons = [];
for (let i = 8; i < 128; i *= 2) {
buttons[i] = new ButtonToPress(i + 'GB');
console.log(buttons[i]);
}
I'm quite new to Javascript and JQuery and i can't seem to figure out why the counter increments for one button but the same code doesn't work for the others. Any help would be greatly appreciated. Thanks!
// JAVASCRIPT/JQUERY (to increment by 1)
let addRoom = document.querySelector(".addRoom");
let subtractRoom = document.querySelector(".subtractRoom");
let input = document.querySelector(".roomsAmmount");
addRoom.addEventListener('click', () => {
input.value = parseInt(input.value) + 1;
});
subtractRoom.addEventListener('click', () => {
input.value = parseInt(input.value) - 1;
});
/*.addRoom,.subtractRoom{position:absolute;font-size:1.3em;width:50%;height:45px;z-index:1;display:flex}.roomsAmmount{position:absolute;width:50px;height:45px;right:-60px;border:none;border-bottom:1px solid #f4b494;outline:0;font-size:1.4em;text-align:center;background-color:transparent;color:#fff}
*/
<div class="bedroomsButton">Bedrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
<div class="bathroomsButton">Bathrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
Document.querySelector()
The Document method querySelector() returns the first Element within the document that matches the specified selector, or group of selectors
You can target all the elements with querySelectorAll(), then loop through them using forEach() to attach the event handler.
// JAVASCRIPT/JQUERY (to increment by 1)
let addRoom = document.querySelectorAll(".addRoom");
let subtractRoom = document.querySelectorAll(".subtractRoom");
addRoom.forEach((el)=>{
el.addEventListener('click', (e) => {
let input = e.target.closest('.bedroomsButton,.bathroomsButton').querySelector(".roomsAmmount");
input.value = parseInt(input.value) + 1;
});
});
subtractRoom.forEach((el)=>{
el.addEventListener('click', (e) => {
let input = e.target.closest('.bedroomsButton,.bathroomsButton').querySelector(".roomsAmmount");
input.value = parseInt(input.value) - 1;
});
});
<div class="bedroomsButton">Bedrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
<div class="bathroomsButton">Bathrooms
<div class="addRoom">
<span class="plusOne">+</span>
</div>
<div class="subtractRoom">
<span class="subtractOne">-</span>
</div>
<input class="roomsAmmount" type="number" value="0">
</div>
like the comment says, document.querySelector only returns either first element or null.
Do so:
let addRoom = document.querySelectorAll(".addRoom");
let subtractRoom = document.querySelectorAll(".subtractRoom");
addRoom.forEach(btn => {
setInputValue(btn, true)
}
)
subtractRoom.forEach(btn => {
setInputValue(btn, false)
}
)
function setInputValue(btn, plus) {
const input = btn.parentNode.querySelector('input')
btn.addEventListener('click', () => {
const originalValue = parseInt(input.value)
input.value = String(plus ? originalValue + 1 : originalValue - 1)
});
}
add class clickable to every + and -
select all clickable and add event listener on every element
document.querySelectorAll(`.clickable`).forEach(el => el.addEventListener(`click`, handleClick))
handleClick function look like this
const handleClick = (e) => {
// selecting a closest input
const input = e.target.parentElement.parentElement.querySelector(".roomsAmmount");
// parsing value
let newValue = parseInt(input.value);
// if clicked element has class plusOne
if (e.target.classList.contains(`plusOne`)) {
newValue++; // increase
}
// if clicked element has class subtractOne
if (e.target.classList.contains(`subtractOne`)) {
newValue--; // decrease
}
// update value
input.value = newValue;
}
working example https://jsfiddle.net/yqp5zx1b/1/