I am trying to create a shopping cart-like function that displays products found in a database and the user can select the quantity of each product and then add it to a cart. The problem that I am currently facing is that when the user clicks on a plus button, all the quantities change and they all display the clicked total price. This is the code I have :
$('.plus').bind('click', function(e) { $('.minus').prop('disabled', false) var quantity = parseInt($('.quantity').val()); if (!isNaN(quantity) && quantity < 8) { $('.quantity').val(quantity + 1); } else if (quantity == 8) { $('.quantity').val(9); $(this).prop('disabled', true) } var price = parseFloat($('.price').text()).toFixed(2); if (quantity != 9) { if ((price * quantity) % 1 !== 0) { $(".total_price").val(parseFloat(price * (quantity + 1)) + ".00"); } else { $(".total_price").val(parseFloat(price * (quantity + 1)) + "0"); } $(".total_price").css("font-weight", "Bold"); $(".total_price").css("color", "brown"); } else { if ((price * quantity) % 1 !== 0) { $(".total_price").val(parseFloat(price * 9) + ".00"); } else { $(".total_price").val(parseFloat(price * 9) + "0"); } $(".total_price").css("font-weight", "Bold"); $(".total_price").css("color", "brown"); } }); $('.minus').bind('click', function(e) { $('.plus').prop('disabled', false) var quantity = parseInt($('.quantity').val()); if (!isNaN(quantity) && quantity > 1) { $('.quantity').val(quantity - 1); } else { $(this).prop('disabled', true) $('.quantity').val(0); } var price = parseFloat($('.price').text()).toFixed(2); if ((price * quantity) % 1 !== 0) { $(".total_price").val(parseFloat(price * (quantity - 1)) + ".00"); } else { $(".total_price").val(parseFloat(price * (quantity - 1)) + "0"); } if (quantity != 1) { $(".total_price").css("font-weight", "Bold"); $(".total_price").css("color", "brown"); } else { $(".total_price").css("font-weight", "normal"); $(".total_price").css("color", "black"); } });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class='row' id='product_item'> <div class='col-5' style='margin: auto;'> <input class='product_name' readonly type='text' name='product_name' style='display: inline; width:100%; border:white;' value='".$row[' productName ']."'> </div> <div class='col-1' style='margin: auto;'> <div class='price' style='display: inline;'>".$row['price']."</div>€</div> <div class='col-3' style='display:inline; margin: auto;'> <div class='input_group_button'> <button class='btn plus' type='button'> <img src='img/plus.png'> </button> </div> <input class='input_group_field quantity' readonly type='number' name='quantity' value='0' max=9> <div class='input_group_button'> <button class='btn minus' type='button'> <img src='img/minus.png'> </button> </div> </div> <div class='col-2' style='margin: auto;'><input class='total_price' readonly type='number' name='total_price' style='display: inline; width:65%; border:white;' value='0.00'>€</div> <div class='col-1'> <button class='btn' type='button' id='add_cart'> <img src='img/add-to-cart.png'> </button> </div> </div>
I appreciate any help I can get!
Answer
To make this work you can use the reference to the clicked element, using the this
keyword in the event handler, to traverse the DOM to find the related elements using jQuery’s built in methods such as closest()
and find()
.
In addition there’s a couple of other things which can be addressed.
bind()
has been deprecated. Useon()
instead.- You can combine the
.minus
and.plus
event handlers by using adata
attribute on the buttons to determine which value should be added to the quantity. This reduces the amount of repetition in the code. - You can use
Math.max()
andMath.min()
to set the extents of the quantity field. This makes the code more succinct by removing the need for the lengthyif
condition. - Do not use
id
attributes in content which is repeated throughout the page.id
have to be unique within the DOM. Useclass
attributes instead to group elements by behaviour. - It’s odd to use a block level
div
element and force it to be inline. If you want an inline element use on directly, such as aspan
. - Avoid putting styling rules in your HTML. Put them in an external stylesheet.
With all that said, try this:
$('.btn-inc').on('click', function(e) { let $el = $(this); let inc = $el.data('inc'); $el.closest('.col-3').find('.quantity').val((i, v) => Math.min(Math.max(parseInt(v, 10) + inc, 0), 8)); updatePrice(); }); let updatePrice = () => { $('.row').each((i, row) => { let $row = $(row); let $price = $row.find('.price'); let $quantity = $row.find('.quantity'); $row.find('.total_price').val(parseFloat($price.text() * $quantity.val()).toFixed(2)); }); }
.row { border: 1px solid #CCC; border-radius: 5px; margin: 10px; padding: 10px; } .row > div margin: auto; } .row > .col-3 { display: inline; } input.product_name { display: inline; width: 100%; border: white; } input.total_price { display: inline; width: 65%; border: white; } span.price { display: inline; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!-- item 1 --> <div class="row"> <div class="col-5"> <input class="product_name" readonly type="text" name="product_name" value="productName"> </div> <div class="col-1"> <span class="price">1.00</span>€ </div> <div class="col-3"> <div class="input_group_button"> <button class="btn plus btn-inc" type="button" data-inc="1">+</button> </div> <input class="input_group_field quantity" readonly type="number" name="quantity" value="0" max="9"> <div class="input_group_button"> <button class="btn minus btn-inc" type="button" data-inc="-1">-</button> </div> </div> <div class="col-2"> <input class="total_price" readonly type="number" name="total_price" value="0.00"> € </div> <div class="col-1"> <button class="btn" type="button" class="add_cart">Add to cart</button> </div> </div> <!-- item 2 --> <div class="row"> <div class="col-5"> <input class="product_name" readonly type="text" name="product_name" value="productName"> </div> <div class="col-1"> <span class="price">9.99</span>€ </div> <div class="col-3"> <div class="input_group_button"> <button class="btn plus btn-inc" type="button" data-inc="1">+</button> </div> <input class="input_group_field quantity" readonly type="number" name="quantity" value="0" max="9"> <div class="input_group_button"> <button class="btn minus btn-inc" type="button" data-inc="-1">-</button> </div> </div> <div class="col-2"> <input class="total_price" readonly type="number" name="total_price" value="0.00"> € </div> <div class="col-1"> <button class="btn" type="button" class="add_cart">Add to cart</button> </div> </div>