The Problem:
I can't seem to be able to get a list of buttons to change the value of their corresponding inputs.
I'm trying to code a cart system for my website, pretty standard. It involves a list of item, each of which have a section that includes two buttons for adding or subtracting the quantity of its corresponding item. These buttons surround the input that displays the current quantity (disabled so that the user must use the buttons to control it).
Everything is working, but I can't seem to figure out how to target the corresponding input of a button that is clicked so that it will change the value of that specific input.
What I've Tried:
I've figured out that if I add a [0] at the end of the "quantityInputs" variable then it will target the first input in the list, makes sense. But I don't want it to only target the first input, I want it to target the input that the button is associated with. Even then, when I test the plus button it only updates the input one time to 2 and then stops, which I only know because I'm printing the value to the console - it doesn't actually change the display on the webpage.
I've considered using a for loop to get the position of the input, but I'm not sure how to incorporate that into the code in order for it to work with the for loop that gets the position of the plus or minus buttons, or if that will even work.
Can anyone please help me out? I'd really appreciate it.
My Code:
HTML:
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', ready)
} else {
changeQuantity()
}
function changeQuantity() {
var buttonUps = document.getElementsByClassName('item-quantity-up')
console.log('There are ' + buttonUps.length + ' plus buttons total.') // Tells you how many plus buttons there are.
for (var i = 0; i < buttonUps.length; i++) { // Get the positions of each plus button.
var button = buttonUps[i]
button.addEventListener('click', addQuantity) // When a plus button is clicked, run the addQuantity function.
}
var buttonDowns = document.getElementsByClassName('item-quantity-down')
console.log('There are ' + buttonDowns.length + ' minus buttons total.') // Tells you how many minus buttons there are.
for (var i = 0; i < buttonDowns.length; i++) { // Get the positions of each minus button.
var button = buttonDowns[i]
button.addEventListener('click', subQuantity) // When a minus button is clicked, run the subQuantity function.
}
}
function addQuantity(event) {
var quantityInputs = document.getElementsByClassName('cart-item-quantity') // Needs an index such as [0] in order to return a real number.
var inputValue = parseInt(quantityInputs.value) // Turns the value into an integer.
var max = quantityInputs.max // Retrieves the max value for the input.
if (inputValue >= max) {
inputValue = inputValue // If the quantity is greater than or equal to the max, then don't change the quantity.
} else {
inputValue = inputValue + 1 // If the quantity is less than the max, then add 1 to the quantity.
console.log(inputValue)
}
}
function subQuantity(event) {
var quantityInputs = document.getElementsByClassName('cart-item-quantity') // Needs an index such as [0] in order to return a real number.
var inputValue = parseInt(quantityInputs.value) // Turns the value into an integer.
var min = quantityInputs.min // Retrieves the min value for the input.
if (inputValue <= min) {
inputValue = inputValue // If the quantity is less than or equal to the min, then don't change the quantity.
} else {
inputValue = inputValue - 1 // If the quantity is greater than the min, then subtract one from the quantity.
console.log(inputValue)
}
}
<div class="cart-quantity-field">
<button class="item-quantity-down">-</button>
<input class="cart-item-quantity" type="number" min="1" max="20" value="1" disabled>
<button class="item-quantity-up">+</button>
</div>
You can manipulate the correct input field by doing the following:
On quantity-button click:
locate the clicked button's parent element
inside that parent, look for input with the correct class name
decide whether the button clicked should add or subtract one
add +1 or -1 respectively to the current value of the input
const allQuantityBtns = document.getElementsByClassName("quantity-btn");
Array.from(allQuantityBtns).forEach(btn => {
btn.addEventListener("click", e => {
const inputToChange = e.target.parentElement.querySelector(".cart-item-quantity");
const plusOrMinusOne = e.target.classList.contains("item-quantity-up") ? 1 : -1;
const newValue = parseInt(inputToChange.value) + plusOrMinusOne;
// validate newValue here. Eg: is it allowed to be smaller than zero?
inputToChange.value = newValue;
});
});
<div class="cart-quantity-field">
<button class="item-quantity-down quantity-btn">-</button>
<input class="cart-item-quantity" type="number" min="1" max="20" value="1" disabled>
<button class="item-quantity-up quantity-btn">+</button>
</div>
Note: you may wish to implement some validation:
stop the quantity from being too large or small before modifying the input's value
Related
Looking for some help with a project I am working on, guys and gals.
I have a simple keypad with the digits from 0 to 9, each one is a separate element.
My goal is to bind onclick() events on all of the elements, and make them insert the respective digit into an input field of my choosing.
I know how to set the value of a certain element ( in this case the input field ), but I don't know how to preserve the previously entered digits without rewriting the value of the entire field with the next clicked digit on the keypad.
document.getElementById("field").value = "digit-here"
This is how I set the whole value field. How do I insert a digit without erasing the previously entered ones?
You can do it with the following:
document.getElementById("field").value += "digit-here"
Basically just add + before the equal sign (=).
Get the existing value, add the digit to the end, set that value. += is happy to do that for you with value:
function handler(e) {
document.getElementById("field").value += this.value;
}
for (let n = 1; n < 10; ++n) {
const btn = document.createElement("input");
btn.type = "button";
btn.value = n;
btn.addEventListener("click", handler);
document.body.appendChild(btn);
}
<input type="text" id="field">
<br>
Doing that when the text field had focus might be problematic because the insertion point would get moved, but since clicking the button will make the text field lose focus anyway, that's not an issue in this simple situation.
Try this out as #T.J Crowder said
var text = document.getElementById("field").value = "";
text.value += '';
I am building a webshop with WooCommerce.
We are using WooCommerce advanced quantity so the quantity is not like 1,2,3,4 etc but in steps of 0.72 for example. When using the +/- button it is working fine. (steps: 0.72, 1.44, 2.16 etc)
When I fill in 20 with my keyboard and click add to my cart in my cart there has been add a quantity of 19.44 instead of 20 because 20 is not an possible option. I want the field to autocorrect to the closest possible quantity value before adding it to my cart. So more like autocorrect it after deselecting the field.
How can I achieve this? Has it something to do with javascript?
Hope you guys can help me!
Link to example page so you can test it: https://www.stenenentegels.nl/product/trommelsteen-chelsea-20x30x4/
You can just set the value to the current value minus itself with the modulus operator. You can determine the best event, but blur would probably work best.
let qty = document.getElementById('qty');
qty.onblur = function(){
let val = this.value; // Current Value
let step = this.getAttribute('step'); // Get step instead of hard coding it
this.value = (val - (val % step)).toFixed(2); // Simple math that adjusts the value on blur
}
<input type="number" id="qty" step=".72" />
To round up one step, you could consider:
let qty = document.getElementById('qty');
qty.onblur = function(){
let val = this.value; // Current Value
let step = this.getAttribute('step'); // Get step instead of hard coding it
let roundDown = (val - (val % step)).toFixed(2);
let roundUp = (parseFloat(roundDown) + parseFloat(step)).toFixed(2);
this.value = roundUp;
}
<input type="number" id="qty" step=".72" />
If you only want this to effect steps that aren't 1, just wrap that in an if statement:
let qty = document.getElementById('qty');
qty.onblur = function(){
let val = this.value; // Current Value
let step = this.getAttribute('step'); // Get step instead of hard coding it
if( step != 1 ){
let roundDown = (val - (val % step)).toFixed(2);
let roundUp = (parseFloat(roundDown) + parseFloat(step)).toFixed(2);
this.value = roundUp;
}
}
<input type="number" id="qty" step=".72" />
You could do it in the blur event of the #qty field with javascript:
jQuery("#qty").on("blur", function(){
correctValue = jQuery(this).val() - (jQuery(this).val() % 0.72);
jQuery(this).val(correctValue.toFixed(2));
})
Edit: You need to find a place to put this - in a WooCommerce template or in the general js file of the site.
Edit 2: You can check that this actually works by pasting the above code in the Chrome JS console and then entering "20" in the field and clicking out of the field again.
I am using number field in html5,
here is my code,
<input type="number" class="edit-items" />
i set the max of this number field using javascript.
element.max = quantity;
element.min = 0;
element.step = 1;
If i am using the arrow-up in the number field. Greater value than the max is not possible. But when I tried to insert in the field, greater value than the max is possible.
I would like to prevent it. How?
Like you said input of type number will work fine with arrows and not with manual change so try to use oninput event to help you control user inputs :
document.getElementsByClassName('edit-items')[0].oninput = function () {
var max = parseInt(this.max);
if (parseInt(this.value) > max) {
this.value = max;
}
}
<input type="number" class="edit-items" max='10'/>
Hope this helps.
Add an event listner on the input (oninput) :
check it here : on change sample
function maxValue(){
var numbers = document.getElementById("numbers");
console.log(numbers);
var maxQuantity = 5;
numbers.addEventListener("input", function(e) {
if(this.value>maxQuantity) {
alert("max value reached ! ");
this.value = maxQuantity;
}
})
};
window.onload = maxValue();
I have a modal that displays stock information for a specific item that has multiple locations within a warehouse. The user selects the locations and quantities from each menu and clicks confirm, the information from this modal needs to then be imported on a pick list which is printed out.
To do this I was planning to use arrays to transport the data to the pick list.
I have a hidden field for each row, containing the values of the Location and the Qty Picked from there.
Location 1 + Qty 1 = Hidden Field 1
Location 2 + Qty 2 = Hidden Field 2
I now want to be able to put those hidden fields into an array once a button is clicked.
Hidden Field 1 + Hidden Field 2 = Array.
I can create the hidden fields just fine, its when I go to make the final array that contains all the data, it only seems to want to add the newest hidden field to be created into it.
Dialog - Pick Quantity Button (Used to confirm the selections):
//PICK QUANTITY Button
'Pick Quantity': function() {
jQuery('.ui-dialog button:nth-child(1)').button('disable');
//Disables the current selection, so that it cannot be editted
$('#AddLocQtyPick'+Picker).prop ('disabled', true);
//Disables the current selection, so that it cannot be editted
$('#LocationPickerSelect'+ Picker).prop ('disabled', true);
//Adds Unique Number to the ID of the input fields
Picker++;
//For Loop that helps to total up the quanities being selected in each picker
total=0;
for (i = 0; i<Picker; i++) {
total= total + $('#AddLocQtyPick'+i).val() * 1.0;
}
//Variable decides max value of pick on appends using previous selection
QtyReqTot= QtyReq - total;
//"Pick Another location" button is enabled whilst Qty Req has not been met
if (total !== QtyReq){
jQuery('.ui-dialog button:nth-child(2)').button('enable');
}
//"Pick Quantity", "Pick Another Location" are disabled, whilst "Confirm" button is enabled when total reaches Qty Req
if (total == QtyReq){
jQuery('.ui-dialog button:nth-child(2)').button('disable');
jQuery('.ui-dialog button:nth-child(1)').button('disable');
jQuery('.ui-dialog button:nth-child(3)').button('enable');
}
//Pick Another Location button is disabled if no more locations to pick from
if (length == 1){
jQuery('.ui-dialog button:nth-child(2)').button('disable');
}
if (total !== QtyReq && length == 1){
jQuery('.ui-dialog button:nth-child(1)').button('disable');
$(":button:contains('Cancel')").focus();
}
//Create Hidden Field - Location
//for loop that creates the fields
for (i = 0; i<Picker; i++){
HiddenSelection = [$('#LocationPickerSelect'+i).val(),$('#AddLocQtyPick'+i).val()];
var appendHiddenSelection = '<input type="hidden" class="HiddenSelection'+ i +'" value='+HiddenSelection+'>';
$('#AddLocationPicker').append(appendHiddenSelection);
alert(appendHiddenSelection +'This is SelectionField'+i);
}
},
Confirm Button - Used to Generate the Final Array containing previous arrays:
'Confirm': function() {
//Reset the length loop
length = undefined;
//Remove "Multiple Location" icon from the row.
$('#icon'+id).hide();
//Checks "Multiple Location" icon for existence and adds Pick List button when all hidden.
$('img[id^=icon]:visible').length || $('#ProcessPickList').show();
//Change text colour back to blue to have visual confirmation that item is ready for picking
$('#Desc'+id).css('color', '#0000FF');
$('#QtyReq'+id).css('color', '#0000FF');
$('#QtyinStock'+id).css('color', '#0000FF');
//Create Total Array
TotalHiddenArray = [HiddenSelection]
alert (TotalHiddenArray);
$(this).dialog('close');
},
I think I need to be able to create unique IDS for the input fields and show how get them to all be added to the array.
You can try replacing
HiddenArray = [appendHiddenQty, appendHiddenLocation]
By
HiddenArray[HiddenArray.length] = [appendHiddenQty, appendHiddenLocation]
This way, instead of overwriting HiddenArray within the loop, you just add [appendHiddenQty, appendHiddenLocation] at the end of HiddenArray.
EDIT1:
Replace
HiddenSelection = [$('#LocationPickerSelect'+i).val(),$('#AddLocQtyPick'+i).val()];
by
HiddenSelection[HiddenSelection.length] = [$('#LocationPickerSelect'+i).val(),$('#AddLocQtyPick'+i).val()];
Or, you also can use push :
HiddenSelection.push([$('#LocationPickerSelect'+i).val(),$('#AddLocQtyPick'+i).val()]);
Please see this quickly made Fiddle
EDIT2:
Ok, so let's try to replace the whole loop by:
var HiddenSelection = new Array;
for (i = 0; i<Picker; i++){
HiddenSelection = [$('#LocationPickerSelect'+i).val(),$('#AddLocQtyPick'+i).val()];
var appendHiddenSelection = '<input type="hidden" class="HiddenSelection'+ i +'" value='+HiddenSelection+'>';
$('#AddLocationPicker').append(appendHiddenSelection);
alert(appendHiddenSelection +'This is SelectionField'+i);
TotalHiddenArray.push([HiddenSelection]);
}
You just have to remove this from your confirm function :
//Create Total Array
TotalHiddenArray = [HiddenSelection]
You also have to delcare TotalHiddenArray as new array outside any function (at the very top of your JS code for exemple, because I guess you are trying to access TotalHiddenArray from another function than the loop) like this :
var TotalHiddenArray= new Array;
Another Fiddle
First, please take a look at my fiddle.
I'm trying to figure out a clean way of making the price next to each item change when any item is selected (in that group, you can image that there will be graphics cards etc in a different section which also will need the same functionality).
If its positive I need the class to be .positive and vice versa, and if the item is selected (+0) then the price difference wont be displayed.
This will also be used on checkbox's.
Non-working example.
You'll want to compare each selected item with items having the same name. In the .each() loop in CalculatePrice(), pass the checked item to this function:
function CalculateDiffs(item) {
var selectedPrice = +item.data("price");
item.siblings(".item_price").text("");
$(".calculation-item[name='"+item.attr("name")+"']").not(item).each(function(){
var price = +$(this).data("price");
var diff = (price-selectedPrice).toFixed(2);
if (diff >= 0) {
diff = "+"+diff;
}
$(this).siblings(".item_price").toggleClass("negative", diff < 0).text(diff);
});
}
As for checkboxes, the above function will take care of hiding the price when it is checked. To display the prices for unchecked checkboxes:
$(".calculation-item:checkbox:not(:checked)").each(function(){
$(this).siblings(".item_price").text("+"+$(this).data("price"));
});
Or, if you want to display the price of a checked checkbox as negative, use this instead:
$(".calculation-item:checkbox").each(function(){
var diff = (this.checked ? "-" : "+") + $(this).data("price");
$(this).siblings(".item_price").toggleClass("negative",this.checked).text(diff);
});
http://jsfiddle.net/gilly3/HpEJf/8/
Actually it's pretty straight forward, all you'll need to do is calculate the difference between the selected price and the price of all the options in the list. Eg, something like this:
$(".calculation-item").each(function(index) {
var my_cost = base_price + $(this).data("price");
var difference = Math.round(my_cost - base_cost);
});
I've created a working jsFiddle for you here: http://jsfiddle.net/HpEJf/6/. You'll need to implement decimal rounding etc but this should put you on the right track :)
If my understanding is correct, you want to display the cost difference from the previously selected radio button and the currently selected radio button.
To do that you need to keep track of the previously selected button. The only way I know of to do that is to set a variable outside the clickhandler scope to keep track of it and update the element in the clickhandler.
The rest is fairly straightforward. I updated your jsFiddle with an example of how to do it. The relevant code is below:
Adding at top of script:
//global for last checked/selected radio
var lastSelection = $(".calculation-item:checked");
//clear existing price diffs set by markup
$('span.processor_price').text('');
Added another function:
function priceDifference(oldPrice, newPrice) {
var difference = {
'cssClass': '',
'inCost': '0'
};
var fixedDiff = '';
var diff = newPrice - oldPrice;
diff = Math.ceil(Math.abs(diff * 100)) / 100;
fixedDiff = diff.toString();
if (newPrice < oldPrice) {
difference.cssClass = 'negative';
difference.inCost = '-' + fixedDiff;
} else if (newPrice > oldPrice) {
difference.cssClass = 'positive';
difference.inCost = '+' + fixedDiff;
}
/* else {
* must be the same, no reason for this block
* as the default empty string will suffice
* as will the cost difference of 0
}*/
return difference;
}
And changed your click handler to:
$(".calculation-item").click(function() {
var difference = {};
if (lastSelection) {
//get difference
difference = priceDifference($(lastSelection).data("price"), $(this).data("price"));
//change class
$(this).siblings('span.processor_price').addClass(difference.cssClass).text(difference.inCost);
$(lastSelection).siblings('span.processor_price').removeClass('positive').removeClass('negative').text('');
if (lastSelection !== this) {
lastSelection = this;
}
} else {
lastSelection = this;
}
CalculatePrice();
});