Increment / Decrement quantity number on WooCommerce using jQuery - javascript

I'm using the following to add + and - buttons to my WooCommerce store. It's working, except once clicked it will change every item quantity in the cart instead of just the specific product. I can't seem to solve it.
Here's the HTML:
<div class="quantity">
<label for="quantity5432">Quantity</label>
<input type="number" id="quantity5432" class="qty" step="1" min="0" name="cart[73d][qty]" value="0">
</div>
<div class="quantity-nav">
<div class="quantity-button add-action add-up">+</div>
<div class="quantity-button add-action add-down">-</div>
</div>
And here's the jQuery
$(document).ready(function() {
const minus = $('.add-down');
const plus = $('.add-up');
const input = $('.qty');
minus.click(function(e) {
e.preventDefault();
var value = input.val();
if (value > 1) {
value--;
}
input.val(value);
});
plus.click(function(e) {
e.preventDefault();
var value = input.val();
value++;
input.val(value);
})
});```

You can adjust your script to target the specific input with the class .qty by selecting this input in the minus and add click() events based on the location of the click.
$(document).ready(function() {
const minus = $('.add-down');
const plus = $('.add-up');
minus.click(function(e) {
e.preventDefault();
const input = $(this).closest(".quantity-nav").prev(".quantity").find(".qty");
var value = input.val();
if (value > 1) {
value--;
}
input.val(value);
});
plus.click(function(e) {
e.preventDefault();
const input = $(this).closest(".quantity-nav").prev(".quantity").find(".qty");
var value = input.val();
value++;
input.val(value);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="quantity">
<label for="quantity5432">Quantity</label>
<input type="number" id="quantity5432" class="qty" step="1" min="0" name="cart[73d][qty]" value="0">
</div>
<div class="quantity-nav">
<div class="quantity-button add-action add-up">+</div>
<div class="quantity-button add-action add-down">-</div>
</div>
<div class="quantity">
<label for="quantity5433">Quantity</label>
<input type="number" id="quantity5433" class="qty" step="1" min="0" name="cart[73e][qty]" value="0">
</div>
<div class="quantity-nav">
<div class="quantity-button add-action add-up">+</div>
<div class="quantity-button add-action add-down">-</div>
</div>

Related

create dropdown with multiple selection in JS

I want to implement a dropdown similar to the one on Booking.com in terms of functionality (I attach a screenshot), but I am encountering some issues and I can't figure out where I'm going wrong. Do you have any suggestions?
HTML
<div class="dropdown">
<input type="text" id="droptxt" class="list" readonly placeholder="Number of guests">
<div id="content" class="content">
<div class="list">
<input type="checkbox" id="rooms" class="list" value="Choose how many rooms" />
<label for="Choose how many rooms" class="list">Choose how many rooms </label>
<input type="hidden" class="list quantity" min="1" value="1" />
</div>
<div class="list">
<input type="checkbox" id="adults" class="list" value="Choose the number of adults" />
<label for="Choose the number of adults" class="list">Choose the number of adults </label>
<input type="hidden" class="list quantity" min="1" value="1" />
</div>
<div class="list">
<input type="checkbox" id="children" class="list" value="Choose the number of children" />
<label for="Choose the number of children" class="list">Choose the number of children </label>
<input type="hidden" class="list quantity" min="1" value="1" />
</div>
</div>
</div>
JavaScript
const txt = document.getElementById('droptxt');
console.log(txt);
const content = document.getElementById('content');
console.log(content);
const checkboxes = document.querySelectorAll('.list input[type="checkbox"]');
const quantity = document.querySelectorAll('.list input[type="number"]');
txt.addEventListener('click', function() {
content.classList.toggle('show');
});
// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
if (!e.target.matches('.list')) {
if (content.classList.contains('show')) content.classList.remove('show');
}
};
checkboxes.forEach(function(checkbox, index) {
checkbox.addEventListener('click', function() {
quantity[index].type = (checkbox.checked) ? 'number' : 'hidden';
calc();
});
});
quantity.forEach(function(input) {
input.addEventListener('input', calc);
});
function calc() {
let arr = [];
for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
arr.push(quantity[i].value + ' x ' + checkboxes[i].value);
}
}
txt.value = arr.join(', ');
}
const quantity = document.querySelectorAll('.list input[type="number"]');
in this line you are selecting input[type="number"] but in your html there is no input which type is number. use this
const quantity = document.querySelectorAll('.list input[type="hidden"]');
it will solve your problem

Combined total for multiple Sliders (Bootstrap v4 & jQuery)

I'm trying to implement a page with 3 sliders. The combined total of all 3 sliders should never go over 100%.
The page uses the Bootstrap 4 framework and jQuery 3.6.2
These is where I struggle:
The combined value of all three sliders should never be over 100%. Increasing one slider should decrease the other two values and vice versa.
Here is what I have done so far:
HTML
<div id="sliders">
<div class="row">
<label for="Focus1" class="form-label">Slider 1</label>
</div>
<div class="row">
<input id="sl1" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="34" data-value="34" value="34" style="display: none;">
<span id="sl1CurrentSliderValLabel"> <span class="value" id="sl1SliderVal">34</span>%</span>
</div>
<div class="row">
<label for="Focus2" class="form-label">Slider 2</label>
</div>
<div class="row">
<input id="sl2" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
<span id="sl2CurrentSliderValLabel"> <span class="value" id="sl2SliderVal">33</span>%</span>
</div>
<div class="row">
<label for="Focus3" class="form-label">Slider 3</label>
</div>
<div class="row">
<input id="sl3" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
<span id="sl3CurrentSliderValLabel"> <span class="value" id="sl3SliderVal">33</span>%</span>
</div>
</div>
JS
`
<script>
$(document).ready(function() {
$('.tooltip').tooltip();
/* Slider 1 */
$("#sl1").slider();
$("#sl1").on('slide', function(slideEvt) {
$("#sl1SliderVal").text(slideEvt.value);
});
/* Slider 2 */
$("#sl2").slider();
$("#sl2").on('slide', function(slideEvt) {
$("#sl2SliderVal").text(slideEvt.value);
});
/* Slider 3 */
$("#sl3").slider();
$("#sl3").on('slide', function(slideEvt) {
$("#sl3SliderVal").text(slideEvt.value);
});
});
/* Start Slider Logic */
var sliders = $("#sliders .slider");
var availableTotal = 100;
sliders.each(function() {
var init_value = parseInt($(this).text());
$(this).siblings('.value').text(init_value);
$(this).empty().slider({
value: init_value,
min: 0,
max: availableTotal,
range: "max",
step: 2,
animate: 0,
slide: function(event, ui) {
// Update display to current value
$(this).siblings('.value').text(ui.value);
// Get current total
var total = 0;
sliders.not(this).each(function() {
total += $(this).slider("option", "value");
});
total += ui.value;
var delta = availableTotal - total;
// Update each slider
sliders.not(this).each(function() {
var t = $(this),
value = t.slider("option", "value");
var new_value = value + (delta/2);
if (new_value < 0 || ui.value == 100)
new_value = 0;
if (new_value > 100)
new_value = 100;
t.siblings('.value').text(new_value);
t.slider('value', new_value);
});
}
});
});
</script>`
CSS
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/bootstrap-slider.css" rel="stylesheet">
Thanks a lot for your help!
The main problem was to use the correct API call at the right time.
This is the correct code to make the sliders work as intended:
JS
`
<script>
$(document).ready(function() {
$('.tooltip').tooltip();
var sliders = $("#sliders .slider");
var availableTotal = 100;
sliders.each(function() {
var init_value = parseInt(this.value);
$(this).siblings('.value').text(init_value);
$(this).empty().slider({
value: init_value,
min: 0,
max: availableTotal,
range: false,
step: 2,
animate: 0
});
$(this).on( "slide", function( event ) {
var slVal = parseInt($(this).val());
// Update display to current value
$(this).siblings('.value').text(slVal);
// Get current total
var total = 0;
sliders.not(this).each(function() {
total += parseInt($(this).val());
});
// Need to do this because apparently jQ UI
// does not update value until this event completes
total += slVal;
var delta = availableTotal - total;
// Update each slider
sliders.not(this).each(function() {
var t = $(this),
value = parseInt(t.slider('getValue'));
var new_value = parseInt(value + (delta/2));
if (new_value < 0 || slVal == 100)
new_value = 0;
if (new_value > 100)
new_value = 100;
t.siblings('.value').text(new_value);
t.slider('setValue',new_value);
});} );
});
});
</script>`
HTML
<div id="sliders">
<div class="row g-6">
<label for="Focus1" class="form-label">Slider 1</label>
</div>
<div class="row g-6">
<input class="slider" id="sl1" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="34" data-value="34" value="34" style="display: none;">
<span class="value" id="sl1SliderVal">34</span><span id="sl1CurrentSliderValLabel">%</span>
</div>
<div class="row g-6">
<label for="Focus2" class="form-label">Slider 2</label>
</div>
<div class="row g-6">
<input class="slider" id="sl2" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
<span class="value" id="sl2SliderVal">33</span><span id="sl2CurrentSliderValLabel">%</span>
</div>
<div class="row g-6">
<label for="Focus3" class="form-label">Slider 3</label>
</div>
<div class="row g-6">
<input class="slider" id="sl3" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
<span class="value" id="sl3SliderVal">33</span><span id="sl3CurrentSliderValLabel">%</span>
</div>
</div>
The CSS section from the question is unchanged.

Vanilla Javascript - add amount of input to total value with different minimum values on inputs

I have three different inputs inside a modal that represents three different "stock" items. if a user adds a value in the input related to that specific item with a minimum of the value stipulated I want to add that input value to the total amount. but I'm stuck with looping over the buttons and calling the addAmount function to get the correct input.
I might be overcomplicating things as I'm quite new to this.
any guidance would be appreciated
const btns = document.querySelectorAll('.add-stock');
const inputs = document.querySelectorAll('[data-stock-amount]');
let totalAmount = document.querySelector('.total-amount');
totalValue = 5000;
const addAmount = (e) => {
inputs.forEach(input => {
let setAmount = input.value;
let inputData = input.dataset.stockAmount;
if (inputData === "stock25") {
totalValue = parseFloat(totalValue) + parseFloat(setAmount);
totalAmount.innerHTML = totalValue;
}
if (inputData === "stock50") {
totalValue = parseFloat(totalValue) + parseFloat(setAmount);
totalAmount.innerHTML = totalValue;
}
if (inputData === "stock100") {
totalValue = parseFloat(totalValue) + parseFloat(setAmount);
totalAmount.innerHTML = totalValue;
}
});
}
btns.forEach(btn => {
btn.addEventListener('click', (e) => {
let btnData = e.target.dataset.addStock;
if (btnData === inputData) {
addAmount(e);
}
});
});
<div class="total">$ <span class="total-amount">5000</span></div>
<div class="modal">
<div class="card">
<input type="number" name="stock1" id="stock1" value="25" data-stock-amount="stock25">
<label for="stock1">Stock1</label>
<button class="add-stock" data-add-stock="stock25">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock2" id="stock2" value="50" data-stock-amount="stock50">
<label for="stock2">Stock2</label>
<button class="add-stock" data-add-stock="stock50">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock3" id="stock3" value="100" data-stock-amount="stock100">
<label for="stock3">Stock3</label>
<button class="add-stock" data-add-stock="stock100">Add Stock</button>
</div>
</div>
Your code is significantly longer and more complicated than it needs to be. Here's a way to get you started:
const btns = document.querySelectorAll(".add-stock");
const totalAmount = document.querySelector(".total-amount");
/* Increment the total amount by the given amount. */
const addAmount = amount => {
// The '+' before the variable casts the value to a number.
totalAmount.innerHTML = +totalAmount.innerHTML + +amount;
}
btns.forEach(btn => {
btn.addEventListener("click", e => {
/* Cache the stock amount of the clicked button. */
let btnData = e.target.dataset.addStock;
/* Use the above value to find the related input. */
let input = document.querySelector("[data-stock-amount = '" + btnData + "']");
/* Parse the stock amount to find the default value. */
let defaultValue = +btnData.replace("stock", "");
/* If the current value exceeds or equals the default, add it. */
if (input.value >= defaultValue) addAmount(input.value);
/* Otherwise, print a warning on the console. */
else console.log(input.value + " is less than the default of " + defaultValue);
});
});
<div class="total">$ <span class="total-amount">5000</span></div>
<div class="modal">
<div class="card"><input type="number" name="stock1" id="stock1" value="25" data-stock-amount="stock25"><label for="stock1">Stock1</label>
<button class="add-stock" data-add-stock="stock25">Add Stock</button>
</div>
<div class="card"><input type="number" name="stock2" id="stock2" value="50" data-stock-amount="stock50"><label for="stock2">Stock2</label>
<button class="add-stock" data-add-stock="stock50">Add Stock</button>
</div>
<div class="card"><input type="number" name="stock3" id="stock3" value="100" data-stock-amount="stock100"><label for="stock3">Stock3</label>
<button class="add-stock" data-add-stock="stock100">Add Stock</button>
</div>
</div>
I believe this code meets your requirements, please see the code comments for details. This approach requires only 1 event listener to be added to a single parent element of the buttons we want to listener to click events for - this technique is called event delegation and is a cleaner approach than adding many event listeners:
const inputEls = document.querySelectorAll('[data-stock-amount]');
const totalAmountEl = document.querySelector('.total-amount');
// Get the initial values and store them as minimums
const minimumStockValues = Array.from(inputEls).reduce((map, stock) => {
map.set(stock.name, stock.value);
return map;
}, new Map());
// Add an event listener on the modal for button clicks
document.querySelector('.modal').addEventListener('click', event => {
if (event.target.tagName === 'BUTTON') {
// Get the input that corresponds to the button that was clicked
const inputEl = document.querySelector(`[data-stock-amount='${event.target.dataset.addStock}']`);
// Compare the value in the input with the allowable minimum amount and take some action
if (inputEl.value >= minimumStockValues.get(inputEl.name)) {
totalAmountEl.innerHTML = parseInt(totalAmountEl.textContent) + parseInt(inputEl.value);
} else {
console.error(`Minimum amount allowed for ${inputEl.name} is $${minimumStockValues.get(inputEl.name)}`);
}
}
});
<div class="total">$<span class="total-amount">5000</span></div>
<div class="modal">
<div class="card">
<input type="number" name="stock1" id="stock1" value="25" data-stock-amount="stock25">
<label for="stock1">Stock1</label>
<button class="add-stock" data-add-stock="stock25">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock2" id="stock2" value="50" data-stock-amount="stock50">
<label for="stock2">Stock2</label>
<button class="add-stock" data-add-stock="stock50">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock3" id="stock3" value="100" data-stock-amount="stock100">
<label for="stock3">Stock3</label>
<button class="add-stock" data-add-stock="stock100">Add Stock</button>
</div>
</div>
You are overcomplicating the logic. Just grab the input value and default value and get the max of it and add it to totalAmount.
const btns = document.querySelectorAll(".add-stock");
const inputs = document.querySelectorAll("[data-stock-amount]");
let totalAmount = document.querySelector(".total-amount");
btns.forEach((btn) => {
btn.addEventListener("click", (e) => {
const minValue = e.target.dataset.addStock.match(/[\d]+/g)[0];
const inputValue = e.target.parentNode.querySelector("input").value;
let totalAmountData = +totalAmount.textContent;
totalAmount.textContent = totalAmountData + Math.max(minValue, inputValue);
});
});
<div class="total">$ <span class="total-amount">5000</span></div>
<div class="modal">
<div class="card">
<input type="number" name="stock1" id="stock1" value="25" data-stock-amount="stock25">
<label for="stock1">Stock1</label>
<button class="add-stock" data-add-stock="stock25">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock2" id="stock2" value="50" data-stock-amount="stock50">
<label for="stock2">Stock2</label>
<button class="add-stock" data-add-stock="stock50">Add Stock</button>
</div>
<div class="card">
<input type="number" name="stock3" id="stock3" value="100" data-stock-amount="stock100">
<label for="stock3">Stock3</label>
<button class="add-stock" data-add-stock="stock100">Add Stock</button>
</div>
</div>

On input change, get all input values in div and update label

I have the following HTML:
<div class="filters">
<div class="filter-label">6</div>
<div class="filter-inputs">
<input type="number" name="i1" id="i1" value="1" min="0" step="1" />
<input type="number" name="i2" id="i2" value="2" min="0" step="1" />
<input type="number" name="i3" id="i3" value="3" min="0" step="1" />
</div>
</div>
The question:
I'd like to, when any input is changed, I want to get that value and
the value of the other two text boxes and add them all together. Once
I have the total value, I need to then change the text of the label
with the new value.
I also have the following JavaScript (jQuery) to detect when a change has happened, but after that, I can't figure out how to loop through each input in the div and get the value.
$('.filter .filter-inputs input').on('input', function() {
var element = $(this);
// ...
});
I looked at loops like the jQuery each method but I've got no idea how to loop through each input inside the filter-inputs div.
You could create array from inputs using map and get methods and then calculate sum with reduce method.
let inputs = $('.filters input');
let label = $('.filter-label');
inputs.on('input', function() {
let sum = inputs.map(function() {
return $(this).val()
}).get().reduce((r, e) => r + +e, 0);
label.text(sum)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filters">
<div class="filter-label">6</div>
<div class="filter-inputs">
<input type="number" name="i1" id="i1" value="1" min="0" step="1" />
<input type="number" name="i2" id="i2" value="2" min="0" step="1" />
<input type="number" name="i3" id="i3" value="3" min="0" step="1" />
</div>
</div>
Or you could just create array with Array.from method and then use reduce method to sum values.
let inputs = $('.filters input');
let label = $('.filter-label');
inputs.on('input', function() {
label.text(Array.from(inputs).reduce((r, {value}) => r + +value, 0))
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filters">
<div class="filter-label">6</div>
<div class="filter-inputs">
<input type="number" name="i1" id="i1" value="1" min="0" step="1" />
<input type="number" name="i2" id="i2" value="2" min="0" step="1" />
<input type="number" name="i3" id="i3" value="3" min="0" step="1" />
</div>
</div>
Use this script
$('.filter-inputs input').change(function(){
var total = 0;
$('.filter-inputs input').each(function(){
total += parseInt($(this).val());
});
$('.filter-label').html(total);
});
No problem!
$('.filter .filter-inputs input').on('input', function() {
//not needed:
//var element = $(this);
// Obtain numeric entries
var i1 = Number($("#i1").val());
var i2 = Number($("#i2").val());
var i3 = Number($("#i3").val());
// Calculate sum of entries
var total = i1 + i2 + i3;
// Populate label div with sum
$(".filter-label").html(String(total));
});
Good luck!
You don't need jQuery for this task http://jsfiddle.net/72e3jLc0/
let label = document.querySelector('.filter-label');
let inputs = document.querySelectorAll('.filter-inputs input');
inputs.forEach(input => {
input.addEventListener('change', e => {
label.innerText = parseInt(label.innerText) + parseInt(input.value);
});
});
Try using the following approach:
$(document).ready(function() {
var total=0;
$("input[type='number']").change(function() {
$(".results").html("");
var objects = $(".filters").find("input[type='number']");
for (var i = 0; i < objects.length; i++) {
total += parseInt($(objects[i]).val());
}
$(".filter-label").text(total);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filters">
<div class="filter-label">6</div>
<div class="filter-inputs">
<input type="number" name="i1" id="i1" value="1" min="0" step="1" />
<input type="number" name="i2" id="i2" value="2" min="0" step="1" />
<input type="number" name="i3" id="i3" value="3" min="0" step="1" />
</div>
</div>
I know this is an old thread, but I would like to give my contribution.
The id can be replaced with document or "body", and input is not necessary if all your inputs have the class .form-control.
$("#id-of-overall-div-element").on("change", "input, .form-control", function () {
// Do something
});

Get average value from multiple input fields(range)

I have been searching for a solution for days, i am an beginner in javascript.
This is the form i use to input value via sliders.
<div class="formSliderField" style="display:block">
<div class="formIcons" id="formSliderImage1"></div>
<div class="formSlider" id="formSlider1">
<input class="slider" name="cat_pk" type="range" min="0" max="10" step="0.5" width="290px" value="0" onchange="field1(this.value)"/>
</div>
<div class="formMark"><p id="formMark1" class="mark">1</p></div>
</div>
<div class="formSliderField" style="display:block">
<div class="formIcons" id="formSliderImage2"></div>
<div class="formSlider" id="formSlider2">
<input class="slider" name="cat_service" type="range" min="0" max="10" step="0.5" width="290px" value="0" onchange="field2(this.value)"/>
</div>
<div class="formMark"><p id="formMark2" class="mark">1</p></div>
</div>
<div class="formSliderField" style="display:block">
<div class="formIcons" id="formSliderImage3"></div>
<div class="formSlider" id="formSlider3">
<input class="slider" name="cat_prijs" type="range" min="0" max="10" step="0.5" width="290px" value="0" onchange="field3(this.value)"/>
</div>
<div class="formMark"><p id="formMark3" class="mark">1</p></div>
</div>
<div class="formSubmitWrapper">
<div id="formSubmitButton">
<input id="formRecensieSubmit" type="submit" value="Bevestigen"/>
</div>
</div>
I need the input value from these sliders to get an average for further use. This needs to be as dynamic as possible, not after i submit this form.
You can see i use onchange, this is already used in a function to display the input values per slider.
Hope someone can help me with this.
JavaScript :
window.onload = function(){
var elements=document.querySelectorAll('input[type=range]')
for(var i = 0; i < elements.length; i++){
elements[i].addEventListener('change',calcul , false);
}
}
function calcul(){
var elements = document.querySelectorAll('input[type="range"]')
var len = 0
var buf = 0
for(var i=0;i<elements.length;i++) {
if(elements[i].parentNode.parentNode.style.display != 'none'){
buf += parseInt(elements[i].value)
len++
}
}
buf = len === 0 ? 0 : buf/len
console.log(buf)
}
CodeOpen
Maybe you're trying to achieve something like this?
(function (win, doc, undefined) {
"use strict";
var myForm = doc.getElementById("myForm"),
sliders = myForm.getElementsByClassName("slider"),
marks = myForm.getElementsByClassName("mark");
myForm.addEventListener("change", changeHandler);
function changeHandler(e) {
var t = e.target,
i = -1,
sum = 0,
avg = 0;
if (t.nodeName.toLowerCase() === "input" && t.type.toLowerCase() === "range") {
i = [].indexOf.call(sliders, t);
marks[i].innerHTML = t.value;
[].forEach.call(sliders, function (val) {
sum += parseFloat(val.value);
});
avg = parseFloat((sum / sliders.length).toFixed(1));
}
console.log(avg);
}
}(window, window.document));
Look at Demonstration

Categories