I am currently working on a quantity picker for an eCommerce. The quantity picker consists of:
- The minus button
- The quantity input field: every time the user clicks on either of the quantity buttons, the value gets updated
- The quantity text: this is the quantity enclosed in
<span>
tags - The plus button
Here is the HTML layout for the quantity picker:
<div class="product-form__input product-form__quantity">
<label class="form__label">
Quantity
</label>
<button class="quantity__button minus no-js-hidden" name="minus" type="button" disabled>
-
</button>
<input class="quantity__input"
type="number"
name="quantity"
id="Quantity-{{ section.id }}"
min="1"
value="1"
form="{{ product_form_id }}"
>
<span class="quantity__text">1</span>
<button class="quantity__button plus" name="plus" type="button">
+
</button>
</div>
The JavaScript is as shown below, where quantityPicker.init()
is called which includes:
- Updating the value of the quantity input field everytime the user clicks on the plus or minus quantity buttons (call the
quantityPicker.onButtonClick()
function) - When the value of the quantity input field changes, the
quantityPicker.onChange()
function must be called.
// Quantity picker
let
quantityFields = document.querySelectorAll(".quantity__input"),
quantityButtons = document.querySelectorAll(".quantity__button"),
quantityPicker = {
onButtonClick: function (event) {
let
button = event.target,
picker = button.closest(".product-form__quantity"),
quantity = picker.querySelector(".quantity__input"),
quantityValue = parseInt(quantity.value),
max = quantity.getAttribute("max") ? parseInt(quantity.getAttribute("max")) : null
if (button.classList.contains("plus") && (max === null || quantityValue + 1 <= null)) {
quantity.value = quantityValue + 1
}
else if (button.classList.contains("minus")) {
quantity.value = quantityValue - 1
}
},
onChange: function (event) {
let
field = event.target,
picker = field.closest(".product-form__quantity"),
quantityText = picker.querySelector(".quantity__text"),
shouldDisableMinus = parseInt(event.target.value) === parseInt(field.getAttribute("min")),
shouldDisablePlus = parseInt(event.target.value) === parseInt(field.getAttribute("max")),
minusButton = picker.querySelector(".quantity__button.minus"),
plusButton = picker.querySelector(".quantity__button.plus")
quantityText.innerText = event.target.value
if (shouldDisableMinus) {
minusButton.setAttribute("disabled", "disabled")
} else if (minusButton.getAttribute("disabled") === true) {
minusButton.removeAttribute("disabled")
}
if (shouldDisablePlus) {
plusButton.setAttribute("disabled", "disabled")
} else if (plusButton.getAttribute("disabled") === true) {
plusButton.removeAttribute("disabled")
}
},
init: function () {
// when a button is clicked
quantityButtons.forEach(quantityButton => {
quantityButton.addEventListener("click", function (event) {
quantityPicker.onButtonClick(event)
})
})
// when a quantity is changed
console.log(quantityFields)
quantityFields.forEach(quantityField => {
console.log(quantityField)
quantityField.addEventListener("change", function (event) {
console.log("Value changed!")
quantityPicker.onChange(event);
})
})
}
}
quantityPicker.init()
However, the issue I am experiencing is that even though the value changes when I click on the plus or minus buttons (as indicated by the change in value on the screen), the change
event is not working.
The event only fires if I directly type in a value into the input, and then press the Enter / return key. How can I call the quantityPicker.onChange()
function every time the value gets updated, without having to press the return key?
2
Answers
I figured out the solution for this question. The
change
nor theinput
events will not work if they are changed programmatically. In order to trigger the events, I had to use thedispatchEvent()
function to trigger those events programmatically, as shown below:Consider using
input
event. It fires on every input