Calculate discount % on a product in Vanilla JS
The problem can be found here;
var wasPrice = parseFloat(document.querySelector('.product-price__was').textContent.replace(pattern, "")) / 100;
var newPrice = parseFloat(document.querySelector('.product-price__discount').textContent.replace(pattern, "")) / 100;
You are using document.querySelector
, however, you'll need the el
element provided by the forEach
like so:
var wasPrice = parseFloat(el.querySelector('.product-price__was').textContent.replace(pattern, "")) / 100;
var newPrice = parseFloat(el.querySelector('.product-price__discount').textContent.replace(pattern, "")) / 100;
Also, to prevent the length
error, check if the querySelector
has found anything by checking for null
, instead off .length
:
if (el.querySelector('.product-price__discount') !== null) {
Take a look at this improved example:
var pattern = /[^0-9\.]/g;
var price = document.querySelectorAll('.product-slab__price');
price.forEach(function(el){
if (el.querySelector('.product-price__discount') !== null) {
var wasPrice = parseFloat(el.querySelector('.product-price__was').textContent.replace(pattern, "")) / 100;
var newPrice = parseFloat(el.querySelector('.product-price__discount').textContent.replace(pattern, "")) / 100;
var discount = 100 - (newPrice / wasPrice) * 100;
el.querySelector('.discountPercentage').innerHTML = discount.toFixed(0) + "%";
}
});
.product-price__was {
text-decoration: line-through;
}
.product-price__discount {
color: red;
}
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__was">Was €10,00</span>
<span class="product-price__discount">Now €8,00</span>
<span class="discountPercentage" > </span>
</div>
</div>
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__was">€15,00</span>
<span class="product-price__discount">€10,00</span>
<span class="discountPercentage" > </span>
</div>
</div>
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__value">€15,90</span>
<span class="discountPercentage" > </span>
</div>
</div>
How to calculate discount for all products
Spans do not have onload
Perhaps you meant this
$(function() { // on page load
$(".discount").each(function() {
const price = +$(this).closest("div").find(".original").text();
const korting = (Math.round((price - ((price) / 100) * 10)));
$(this).text(korting)
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
<div>$<span class="original">100</span> $<span class="discount"></span></div>
Calculate percentage discount of multiple products on a product listing page
The issue is because you're selecting all the elements in the DOM with those classes as you iterate through the loop.
Instead, you need to target the elements with those classes relevant to the current .product-slab__price
in the iteration. To do that use the this
keyword and the find()
method:
setTimeout(function discountPrice() {
var pattern = /[^0-9\.]/g;
$('.product-slab__price:has(.product-price__value--discounted)').each(function() {
let $slab = $(this);
var wasPrice = parseFloat($slab.find(".product-price__primary .product-price__was").text().replace(pattern, "")) / 100;
var newPrice = parseFloat($slab.find(".product-price__primary .product-price__value--discounted").text().replace(pattern, "") / 100);
var subtractPrice = Math.abs(wasPrice - newPrice);
var dividePrice = Math.abs(subtractPrice / wasPrice);
var multiplyPrice = Math.abs(dividePrice * 100);
$slab.find('.discountPercentage').html(multiplyPrice.toFixed(0) + "%");
});
}, 500);
.product-price__was {
text-decoration: line-through;
}
.product-price__value.product-price__value--discounted {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__was">Was €10,00</span>
<span class="product-price__value product-price__value--discounted">Now €8,00</span>
<span class="discountPercentage"> </span>
</div>
</div>
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__was">€15,00</span>
<span class="product-price__value product-price__value--discounted">€10,00</span>
<span class="discountPercentage"></span>
</div>
</div>
<div class="product-slab__price">
<div class="product-price__primary">
<span class="product-price__value">€15,90</span>
<span class="discountPercentage"></span>
</div>
</div>
How to calculate discount for each item of an array using reduce()?
While looping through the price items array, using reduce
, you are already accessing each item element and calculating the discounted price for each item. So before updating the reduce
accumulator update the item value.
Update each item and add to total:
// CHANGE:
// let totalValue = items.reduce((acc, cur) => acc + (1 - (numDiscount / 100)) * cur.innerText, 0);
// TO:
let totalValue = items.reduce((acc, cur) => {
let itemdisc = (1 - (numDiscount / 100)) * cur.innerText; // calculate item discount
cur.innerText = itemdisc; // update item
return acc + itemdisc; // update total
}, 0);
And prevent discount from being applied again:
document.querySelector('.total__button').disabled = true;
Updated script:
function getDomNodesBySelector(selector) {
return Array.from(document.querySelectorAll(selector));
}
document.querySelector('.total__button').addEventListener('click', applyDiscount);
function applyDiscount() {
let items = getDomNodesBySelector(".price-value");
let numDiscount = 15;
let totalValue = items.reduce((acc, cur) => {
let itemdisc = (1 - (numDiscount / 100)) * cur.innerText; // calculate item discount
cur.innerText = itemdisc; // update item
return acc + itemdisc; // update total
}, 0);
document.querySelector(".total-price-value").innerText = totalValue;
// prevent discount from being applied more than once
document.querySelector('.total__button').disabled = true;
}
Jquery discount calculation from range
For your example (elements of whole_sell_qty
are sorted and values are evenly spaced out) you can get the index of the element in whole_sell_price
like this:
let qty = $(".quantity").val();
let index = Math.floor( (qty-1)/10 );
if(index < 0) {
index = 0;
} else if(index > whole_sell_qty.length-1) {
index = whole_sell_qty.length - 1;
}
If the elements in whole_sell_qty
were not spaced out evenly then you could make a function to figure out the correct price for the given quantity:
function setPrice() {
let qty = $(".quantity").val();
let length = whole_sell_qty.length;
for(let i=0; i < length; i++) {
if(qty > whole_sell_qty[i])
continue;
$("#total").text(qty * whole_sell_price[i]);
return;
}
$("#total").text(qty * whole_sell_price[length-1]);
}
Note: Both of these solutions assume your two arrays whole_sell_qty
and whole_sell_price
have matching elements in the same indexes. If that is not the case you could consider using an object to represent the different quantities and their respective prices.
Try it out:
Note: I added the min
attribute to the input[type="number"]
element so you can't click your way to a value less than 0. But you can still enter a negative number manually in the text field. For that I didn't provide a solution here but you can see how to deal with that here.
var whole_sell_qty = [10, 20, 30];
var whole_sell_price = [10, 9, 8];
$(".quantity").on('click', function () {
let qty = $(".quantity").val();
// this will get the correct price index for whole_sell_price based on qty
let index = Math.floor( (qty-1)/10 );
if(index < 0) {
index = 0;
} else if(index > whole_sell_qty.length-1) {
index = whole_sell_qty.length - 1;
}
$("#total").text(qty * whole_sell_price[index]);
});
table, th, td, tr {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<label>Product name : shoe</label><br>
<label>Product price : </label><input type="number" id="price" value="10" disabled><span>$</span><br>
<label>Qty : </label> <input type="number" name="quantity" min="0" class="quantity" value="0"/><br>
<label>Total : <span id="total">0</span></label>
<table>
<thead>
<tr>
<th>qty range</th>
<th>unit price</th>
</tr>
</thead>
<tbody>
<tr>
<td>1-10</td>
<td>$ 10</td>
</tr>
<tr>
<td>11-20</td>
<td>$ 9</td>
</tr>
<tr>
<td>21-30</td>
<td>$ 8</td>
</tr>
</tbody>
</table>
Related Topics
Scrollable Table With Fixed Headers and Fixed First Column
Change Specific Button Text on Click Inside Ngfor
Remove Double Quotes from Array in Angularjs
Get Docx File Contents Using Javascript/Jquery
How to Set Top and Bottom Margin in Addhtml
Google Charts: Move Legend Position
Eslint: Disable Warning - 'Defined But Never Used' for Specific Function
Prevent Html5 Videos from Downloading the Files on Mobile - Videojs
Setting the Value of a Checkbox Input If Not Checked
Es6/Babel Class Constructor Cannot Be Invoked Without 'New'
Angular Load Async Data Before Component Initialization
Slick Carousel - Force Slides to Have the Same Height
How to Make a Owl Carousel With Arrows Instead of Next Previous
Use Localstorage Variables in PHP
How to Change Font-Size in Vuetify Component
Charts.Js Graph Not Scaling to Canvas Size