I am a beginner in JS and working at a shopping cart. I have several products which are rendered in the page with ES6 template strings. Everything is working so far, you can add items to the basket and the basket and total update correctly. The only part I am having trouble with is the increase/decrease buttons: they only work once, if you click again the amount printed in the console stays the same.
I did find other SO post related to increment/decrement functions but the button keeps working only once, so I reckon the problem is related to something else in the code that I am overlooking.
Please see the code below:
this is the shopping cart that will be rendered
// select ul
const shoppingCart = document.querySelector('.cart-items');
// create a li item inside ul
let billContainer = document.createElement('li');
// attach an event listener to every li
billContainer.classList.add('list');
// create the markup for every item added to the cart
for(let j = 0; j < basket.length; j++){
const billMarkup = `
<p class="prodName">${basket[j].name}</p>
<div class="button-wrapper">
<button type="button" name="increase" class="increase">+</button>
<span class="quantity">${basket[j].quantity}</span>
<button type="button" name="decrease" class="decrease">-</button>
</div>
<p class="totPrice">£${basket[j].price}</p>
`;
// add the markup to the DOM
billContainer.innerHTML = billMarkup;
shoppingCart.appendChild(billContainer);
}
and this is the increase/decrease functionality (the event listener for the buttons is attached to their parent 'li'):
// attach an event listener to every li
const li = document.querySelectorAll('.list');
li.forEach( liItem => liItem.addEventListener('click', updateBill));
// add or remove items on click
function updateBill(e){
if(e.target.nodeName === 'BUTTON'){
// current value in the basket
let value = parseInt(this.querySelector('.quantity').textContent);
// if the user clicks on 'increase' button
if(e.target.name === 'increase'){
value++;
console.log(value);
// if the user clicks on 'decrease' button
} else if(e.target.name === 'decrease'){
value < 1 ? value = 1 : '';
value--;
console.log(value);
}
}
}
Thanks!
Problem
Plus/minus buttons inc/decremented only once then wouldn't go any further.
Explanation
Once a value has changed, it is just a number in a variable floating in the console since that is the last statement that has anything to do with the value. So only the initial change is successful but when the buttons are clicked for the second time, the function goes back to span.quantity and gets the value that's never been updated from the last click.
Solution
The easiest way to resolve the problem at hand is to update the value of span.quantity:
if (e.target.name === 'increase') {
value++;
console.log(value);
} else if (e.target.name === 'decrease') {
value--;
value = value < 1 ? 1 : value;
console.log(value);
} else {
return false;
}
this.querySelector('.quantity').textContent = value;
Because you didn't provide a functional nor a copyable demo, I didn't bother to test it nor did I attempt to spot check your code. It's less effort to rewrite the source and resolve the problem and maybe prevent problems in the future.
Demo Highlights
The Demo uses a different API for referencing form controls and alternate methods and properties that are better versions of ones that are more commonly used. Event Delegation is used. Array methods might've been overkill but I like using them. Below are line item references to the Demo, unfortunately Stack Snippets don't have line numbers. The Plunker - index.html and README.md can be read together with line numbers.
HTMLFormControlsCollection
52 Declare the <form>,
53 Referencing ALL form controls,
92-95 Create very short references to each form control,
96-99 Create references to their values and convert them to numbers,
102-103, 109-110 Simple and short expressions,
122 A grand total value
Template Literals
75-83 Improved the layout for the list items by using semantic elements. Each element is assigned a unique #id,
92-94 Flexible referencing of #ids originating from the results of 89 and 90.
Array Methods
90-91 By planning a specific naming strategy: abc-0, split('-').pop() returns the number end of an id and split('-').shift() returns the letters before the dash,
113-120 Collecting all .prc;
map() returns an array of price totals;
reduce() returns the total sum;
Event Delegation/Event Object
52 Reference the <form>,
54 Register the <form> to click events. This is the only EventListener needed, it will work for all of its children/descendants,
88-91, 100 Reference origin of event with the Event.target property and not only determine the clicked element but others as well like siblings, parents/ancestors, and children/descendants.
Miscellaneous
56-71 It looks like the basket is an array of objects? Didn't see it in the OP so I had to guess. Removed the basket[j].quantity property because it makes more sense that each item is initially a quantity of 1.
84 insertAdjacentHTML() is innerHTML on steroids.
Plunker
Demo
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
font: 400 16px/1.1 Consolas;
}
legend {
font-size: 1.3rem;
}
output,
input {
display: inline-block;
text-align: center;
}
[id^=qty] {
width: 1.5ch;
}
[id^=prc] {
min-width: 9ch;
}
[id^=prc]::before {
content: "= £";
}
[id^=bas]::before {
content: " x £";
}
#cart+label {
display: inline-block;
margin: 10px 0 0 40%;
}
#total::before {
content: " £";
}
</style>
</head>
<body>
<form id='cart'></form>
<label>Total:
<output id='total' form='cart'>0.00</output>
</label>
<script>
var cart = document.forms.cart;
var x = cart.elements;
cart.addEventListener('click', updateBill, false);
var basket = [{
name: "thing0",
price: 1.99
}, {
name: "thing1",
price: 12.99
}, {
name: "thing2",
price: 21.59
}, {
name: "thing3",
price: 0.09
}, {
name: "thing4",
price: 5.99
}];
for (let j = 0; j < basket.length; j++) {
var details = `
<fieldset id="item-${j}">
<legend>${basket[j].name}</legend>
<button id="inc-${j}" type="button">+</button>
<output id="qty-${j}">1</output>
<button id="dec-${j}" type="button">-</button>
<output id="bas-${j}">${basket[j].price}</output>
<output id="prc-${j}" class="prc">${basket[j].price}</output>
</fieldset>
`;
cart.insertAdjacentHTML('beforeend', details);
}
function updateBill(e) {
if (e.target.type === 'button') {
var ID = e.target.parentElement.id;
var idx = ID.split('-').pop();
var dir = e.target.id.split('-').shift();
var qty = x.namedItem(`qty-${idx}`);
var bas = x.namedItem(`bas-${idx}`);
var prc = x.namedItem(`prc-${idx}`);
var sum = x.total;
var quantity = parseInt(qty.value, 10);
var base = parseFloat(bas.value).toFixed(2);
var price = parseFloat(prc.value).toFixed(2);
var total = parseFloat(sum.value).toFixed(2);
if (dir === "inc") {
quantity++;
qty.value = quantity;
prc.value = quantity * base;
} else {
quantity--;
if (quantity <= 0) {
quantity = 1;
}
qty.value = quantity;
prc.value = quantity * base;
}
}
var prices = Array.from(document.querySelectorAll('.prc'));
var numbers = prices.map(function(dig, idx) {
return parseFloat(dig.value);
});
var grandTotal = numbers.reduce(function(acc, cur) {
return acc + cur;
}, 0);
x.total.value = grandTotal.toFixed(2);
}
</script>
</body>
</html>
Related
I am a beginner in Javascript. What I am trying to do is when a user clicks on "Click to start loop", the first <li> will be 1. The second time the user clicks it, it will be 2, and the third time, it will be 3. After the third click, the loop will break.
The issue with my code is that it always displays the number 3 instead of starting from 1 and going all the way to 3.
function myFunction() {
demo = document.getElementById("demo")
ul = document.createElement("ul")
demo.appendChild(ul)
li = document.createElement("li")
ul.appendChild(li)
for (let i = 1; i <= 3; i++){
li.innerText = i
}
}
<p id="demo" onclick="myFunction()">Click to start loop</p>
It is because, there is only one 'li' element created before loop starts and at the end of loop, it is just updating the final innterText.
You can fix it by moving li creation code to loop
function myFunction() {
demo = document.getElementById("demo")
ul = document.createElement("ul")
demo.appendChild(ul)
<--- from here
for (let i = 1; i <= 3; i++){
li = document.createElement("li") <--- to here
ul.appendChild(li)
li.innerText = i
}
}
You just have to save the current loop value in some place:
let i = 1;
function myFunction() {
// Check for i value
if (i === 4) return;
demo = document.getElementById("demo")
ul = document.createElement("ul")
demo.appendChild(ul)
li = document.createElement("li")
ul.appendChild(li)
// Update the i value
li.innerText = i++;
}
<p id="demo" onclick="myFunction()">Click to start loop</p>
One solution could be to create a global variable with initial value set to 1 and increase it every time there is a click on your <p> tag.
I have implemented the same using the global variable counter.
<p id="demo" onclick="myFunction()">Click to start loop</p>
<script>
var counter = 1;
function myFunction() {
if(counter === 4){
return;
}
demo = document.getElementById("demo");
ul = document.createElement("ul");
demo.appendChild(ul);
li = document.createElement("li");
ul.appendChild(li);
li.innerText = counter;
counter++;
}
</script>
I think you should put li = document.createElement("li") inside of the loops
index.js
function myFunction(status) {
demo = document.getElementById("demo")
ul = document.createElement("ul")
demo.appendChild(ul)
for (let i=1; i<=3; i++) {
li = document.createElement("li")
li.innerText = i
ul.appendChild(li)
}
}
This is unclear, are you looking for javascript function generator ?
const
ul_List = document.body.appendChild( document.createElement('ul') )
, generator = (function* ()
{
for (let i = 0; (++i < 4);)
{
ul_List.appendChild( document.createElement('li') ).textContent = i
yield
}
})()
<p id="demo" onclick="generator.next()" >Click to 3 times loop</p>
Counters
A function that deals with an incrementing variable (aka counter) usually declares or defines it as a number outside of a loop then iterates the variable with a ++ or += operator within the loop. But would a loop within an event handler that increments a number by +1 per click make much sense? So forget about iterations based on a single run of a function/event handler.
The next problem is that once the function/event handler is done, the counter is garbage collected (deleted from memory), so on the next click it is back to what it was initially (usually 0) -- so you need the user to click a HTML element, increase a number by one, and increase it by one per click thereafter. There a few ways to keep the counter's last value:
HTML/DOM
Store the last value in a HTML form control by [value]
let counter = 0;
counter++;
document.forms[0].count.value = counter;
....
<input id='count' type='hidden' value='0'><!--value will be '1'-->
Store the last value in any other type of HTML element by [data-*]
or text content
document.querySelector('.count').dataset.idx = counter;
....
<nav class='count' data-idx='1'></nav>
document.querySelector('aside').textContent = counter;
....
<aside>2</aside>
Keep in mind any value stored in HTML is converted into a string so when getting the variable counter value you must convert it back into a real number:
parseInt(document.forms[0].count.value);
parseFloat(document.querySelector('.count').dataset.idx);
Number(document.querySelector('aside').textContent);
Closures
A better way IMO is to deal with variable scope. If you noticed in the previous code, let and const are used instead of var. The reason for this is scope.
Function Scope: If var was used, then all variables would be influenced by anything inside or the immediate outside of the function it is in. If completely outside of all functions then it is global (much more susceptible to side effects and buggy behavior).
Block Scope: let and const scope is block which means they can only be accessed within the brackets they are located in:
var G = `any function, method, expression, etc can affect it or be affected
by it`;
function clickHandler(e) {
var L = `vulnerable to anything within the function and immediately
outside the function`;
if (e.target.matches('button')) {
let A,
const B = `only affects or get affected only by things within the
brackets`;
var C = `even buried deep within a function it will be hoisted to be
accessible to everything within this function`;
}
function addItem(node, count) {
/* Although a function defined within another function it cannot access
A or B*/
}
}
Wrapping a function/event handler in another function in order to provide an isolated scope and an environment wherein variables can exist past runtime and avoid garbage collection is a closure. The following examples are closures #1 is simple and #2 is more refined (user can edit each list item directly). Go here for details on scope and closures.
Example 1
function init() {
const ul = document.querySelector('ul');
let i = 0;
function clickHandler(e) {
i++;
addItem(this, i);
}
ul.onclick = clickHandler;
};
function addItem(list, counter) {
const li = document.createElement('li');
list.append(li);
li.textContent = counter;
};
init();
<ul>Click to Add Item</ul>
Example 2
function init() {
const ui = document.forms[0];
let counter = 1;
function addItem(event) {
const clicked = event.target;
if (clicked.matches('legend')) {
const list = clicked.nextElementSibling;
let marker = `${counter++}`.padStart(2, '0');
list.insertAdjacentHTML('beforeEnd', `<li contenteditable data-idx="${marker}"></li>`);
}
}
ui.onclick = addItem;
};
init();
form {
font: 1.5ch/1 Consolas;
}
legend {
font-size: large;
font-weight: bold;
user-select: none;
cursor: pointer;
}
legend::after {
content: 'Click to Add Item';
font-size: small;
}
ul {
list-style: none;
}
li {
margin: 5px 11px 5px 9px;
padding: 4px 8px;
border-bottom: 1px solid #980;
}
li::marker {
display: list-item;
content: attr(data-idx)'.';
margin-bottom: -2px;
}
<form>
<fieldset>
<legend>List<br></legend>
<ul></ul>
</fieldset>
</form>
I wrote this function to modify the text of my HTML tag when a checkbox is marked.
var price = 15
function checkPrice() {
let extraCheese = document.getElementById("extraCheese");
if (extraCheese.checked = true) {
price += 5;
// console.log(price)
document.getElementById('pizzaPrice').innerHTML = `Current Price: $${price}.00`
}
}
<input type="checkbox" value="extraCheese" onclick="checkPrice(this);" id="extraCheese" />
<label for="extraCheese"> Extra Cheese </label>
<h3 id="pizzaPrice"> Base Price: $15.00 </h3>
It works as intended and adds 5 to the current price, but the checkbox stays checked and each time it is clicked the value adds by five again. What should I do to let the user uncheck the box and reduce the value to what it originally was?
Use else to reduce the price.
There's also no need to call getElementById(), since you pass the element to the function with checkPrice(this)
checked is a boolean property, you don't need to compare it (and you had a typo using = instead of == to compare).
function checkPrice(extraCheese) {
if (extraCheese.checked) {
price += 5;
} else {
price -= 5;
}
// console.log(price)
document.getElementById('pizzaPrice').innerHTML = `Current Price: $${price}.00`
}
you can assign an extra property to your element to check if it's already been increased or not
function checkPrice(extraCheese) {
var price = 15;
function checkPrice() {
let extraCheese = document.getElementById("extraCheese");
if (
(extraCheese.checked && (typeof extraCheese.inc == "undefined" || extraCheese.inc == false))
) {
price += 5;
extraCheese.inc = true;
// console.log(price)
document.getElementById("pizzaPrice").innerHTML = `Current Price: $${price}.00`;
}
}
}
You should be watching for the change event of the checkbox, rather than the click event. Then, from the event itself, you can get a handle to the element, and use that to check whether the element is currently checked or not.
Additionally, I recommend keeping your interaction and presentation logic separate. I.e., don't declare event handlers in your HTML, and don't include too much string formatting stuff in your javascript. Keep it surgical, just modify the specific part of the HTML that needs to change -- it will help to wrap the price number in a <span> tag so that you can change the number without having to repeat the 'Base Price: ' part in your javascript. This attention to detail can help keep things clean when your code gets larger.
var currentPriceElement = document.getElementById('currentPrice');
function getCurrentPrice() {
return Number(currentPriceElement.textContent);
}
function setCurrentPrice(price) {
currentPriceElement.textContent = price.toFixed(2);
}
function onExtraCheeseChange(event) {
var target = event.target;
var price = getCurrentPrice();
if (target.checked) {
price += 5;
}
else {
price -= 5;
}
setCurrentPrice(price);
}
document.getElementById('extraCheese').addEventListener('change', onExtraCheeseChange);
<input type="checkbox" value="extraCheese" id="extraCheese" />
<label for="extraCheese"> Extra Cheese </label>
<h3 id="pizzaPrice"> Base Price: $<span id="currentPrice">15.00</span> </h3>
So I have assigned all my numbers a class of numbers and all my operators a class of operators with ids specific to their operation. Every item is within a div tag.
Full data here: jsfiddle
<div class="number clear" id="clear"><h1>C</h1></div>
<div class="number" id="entry"><input type="number"></div>
<div class="number seven"><h1>7</h1></div>
<div class="number eight"><h1>8</h1></div>
<div class="number nine"><h1>9</h1></div>
<div class="operate divide" id="divide"><h1>/</h1></div>
So the above is just a glimpse of the HTML. My CSS works perfectly fine but I'm struggling with the JavaScript. I've put in a for loop to pull from all the numbers in the HTML to do an addEventListener for onclick. I feel confident in the for loop but I could definitely be wrong.
Right now I have the following:
let number = document.getElementsByClassName("number");
let operate = document.getElementsByClassName("operate");
let entry = document.getElementById("entry");
let clear = document.getElementById("clear");
let sub=document.getElementById("sub");
let multiply = document.getElementById("mul");
let divide = document.getElementById("divide");
let add = document.getElementById("plus");
for (let i=0; i<numbers.length; i++) {
numbers[i].addEventListener("click", function(entry)){
let inputValue = entry[0].innerHTML;
let buttonValue = this.html;
if (buttonValue === "C") {
entry[0].innerHTML = "";
} else {
entry[0].innerHTML += buttonValue;
}
}
}
function (entry){
}
I know I need to run a function in the for loop but for the life of me I'm drawing blanks as to what to enter to push the values from the div into the entry field for the calculation. Right now if I click on any of the buttons nothing happens, clearly as there's no function. Any insight on how to adjust this to get something to populate would be appreciated.
let numbers = document.getElementsByClassName("number");
let entry = document.getElementById("entry");
for (let i=0; i<numbers.length; i++) {
numbers[i].addEventListener("click", function(){
let buttonValue = this.textContent;
if (buttonValue === "C") {
entry.innerHTML = "0000";
} else {
entry.innerHTML = (entry.innerHTML+buttonValue).substr(-4);
}
});
}
.number {
display:inline-table;
}
<h1><div id="entry">0000</div></h1>
<div class="number"><h1>1</h1></div>
<div class="number"><h1>2</h1></div>
<div class="number"><h1>3</h1></div><br>
<div class="number"><h1>4</h1></div>
<div class="number"><h1>5</h1></div>
<div class="number"><h1>6</h1></div><br>
<div class="number"><h1>7</h1></div>
<div class="number"><h1>8</h1></div>
<div class="number"><h1>9</h1></div><br>
<div class="number "><h1>C</h1></div>
You just mixed up some variable names and used non existent properties...
As mentioned by Jonas in his answer, there are a bunch of issues in your current code - including some syntax issues. So I decided replace your event handler completely, and write it using jQuery - as it really comes in handy in such cases.
The event handler looks like this:
$(".number").on("click", function(event){
var num = $(this).text();
console.log(num);
if(num != 'C') {
var currentNumber = $("#entryNum").val();
$("#entryNum").val(currentNumber.toString() + num.toString());
} else {
$("#entryNum").val('');
}
});
The logic of the event handler remains quite similar to your original logic, but I have just simplified it using jQuery. Also, to allow for faster access to the input element, I gave it an ID entryNum:
<div class="number" id="entry"><input type="number" id="entryNum"></div>
Here's the updated fiddle: https://jsfiddle.net/Nisarg0/L9mL4v3a/6/
Not certain about exact logic or expected result, though there are issue with selecting appropriate element and syntax errors
for (let i = 0; i < numbers.length; i++) {
numbers[i].addEventListener("click", function(event) {
// select the `<input>` child element of `entry` here, not `entry[0]`
// and use `.value` property
let inputValue = entry.querySelector("input").value;
// you want `.textContent` here, not `.innerHTML`
let buttonValue = this.textContent;
if (buttonValue === "C") {
entry.innerHTML = "";
} else {
entry.innerHTML += buttonValue;
}
}) // include closing `)` at `.addEventListener()` call
}
I just implement Press and Clear function.
Learn and do it!
1.Add id for inputfield
<div class="number" id="entry"><input id="entryText" type="number"></div>
2.Do it!
let numbers = document.getElementsByClassName("number");
let entryText = document.getElementById("entryText");
for (let i=0; i<numbers.length; i++) {
numbers[i].addEventListener("click", function(event){
let buttonValue = event.toElement.innerText;
if (buttonValue === "C") {
entryText.value = "";
} else {
entryText.value += buttonValue;
}
});
}
There are several issues in your code:
Your event listener accepts a parameter called entry, which overwrites the entry variable in the outer scope. The first parameter of the event listener that you pass to addEventListener contains information about the click event (which you normally don't need). So remove the entry parameter from the event listener.
The value of this in an event listener is a reference to the element that you bound the event listener to. It won't have a property called html.
You declare a variable called number, but use a variable called numbers.
numbers[i].addEventListener("click", function(entry)){ results in a syntax error. Remove the second ) between parameter list and the function body.
function (entry){} results in a syntax error, because function statements require a name. Just remove it, it's superfluous.
That said, get some inspiration from the following code snippet:
const input = document.querySelector('input');
document.querySelectorAll('.number').forEach(function (button, index) {
button.addEventListener('click', function () {
input.value += index + 1;
});
});
<input type="number">
<button class="number">1</button>
<button class="number">2</button>
<button class="number">3</button>
<button class="number">4</button>
I've got this array:
var size = [small, medium, large];
and this element:
<div class="wp-one wp-two wp-small"></div>
How do I change the size class looping through the size array in JQuery on pressing a button? For example if the element has wp-small, change to wp-medium and so forth looping through the array.
.wp-small {
color: #f00;
}
.wp-medium {
color: #0f0;
}
.wp-large {
color: #00f;
}
<div class="wp-one wp-two wp-small">fdgbfbfnghn</div>
<button>CHANGE CLASS</button>
You can do something like this:
var size = ['small', 'medium', 'large'];
var button = document.getElementById("button");
var id= document.getElementById("sentence");
var i = 0;
button.onclick = function(){
sentence.classList.remove("wp-"+size[i]);
(i == size.length - 1) ? i = 0 : i++;
sentence.classList.add("wp-"+size[i]);
}
JSFiddle
It could probably be tidied up but I'm no JS Wizard.
Basically, the first 4 lines are just me putting stuff into variables. Simple stuff.
I then make a function that on the click of button, it removes the class from the element that is current in the size array. It then checks to see what number the i is at (starting at 0) and if it's larger than the length of size, it resets back to the beginning, if not, it goes to the next array element.
It can be done in jQuery too:
$(document).ready(function(){
var size = ['small', 'medium', 'large'],
button = $("#button"),
sentence= $("#sentence"),
i = 0;
button.click(function(){
sentence.removeClass("wp-"+size[i]);
(i == size.length - 1) ? i = 0 : i++;
sentence.addClass("wp-"+size[i]);
});
});
JSFiddle
But will be faster and just as simple in pure JavaScript
is that what you need ?
var state = 0;
var size = ['small', 'medium', 'large'];
btn = document.getElementById('changeSize');
div = document.getElementById('btn');
btn.addEventListener('click', function() {
state = state == size.length - 1 ? 0 : state + 1;
btn.className = 'wp-' + size[state];
});
JSFiddle
You could add this jQuery code:
$(function(){
currSize = 0;
maxSize = (size.length - 1);
$('button').on('click', function(){
$('.wp-one').removeClass('wp-'+size[currSize]);
if (currSize < maxSize){
currSize++;
}
else{
currSize = 0;
}
$('.wp-one').addClass('wp-'+size[currSize]);
});
});
I am not sure what you are trying to accomplish. Do you want to alter through the availyble sizes, so that a wp-small element will be a wp-medium element and so on? And large ones will be small again?
If so try this:
buttonclickHandler = function(e) {
for(i=size.length;i>0;i--) {
var nextIndex = i % size.length;
$(".wp-" + size[i-1]).switchClass(".wp-" + size[i-1],".wp-" + size[nextIndex]);
}
}
Although in this code you'd have the problem that large elements would be first changed to small elements and later on to medium elements (within one single click). So you'd have to remember those initially large ones and exclude them from the small-to-medium-changes.
Due to simplicity I did not include that in the code above.
I'm using the jQuery validate plugin. I'm trying to calculate the sum of all inputs fields using the class .percent. If the sum of the .percent fields isn't equal to 100%, throw validation error.
The percent rows are a part of a dynamic grid and can very in number. You'll see a sample of what I've been working on below. I'm finding the addMethod is called for every input.percent rather than a single time. I'd also like to say it's being called before submit is called.
the code.
html
<div class="row">
<input value="" class="percent"/>
</div>
<div class="row">
<input value="" class="percent"/>
</div>
js
$.validator.addClassRules({
percent: {percent:true}
});
$.validator.addMethod("percent",
function cals(value, element) {
// all the fields that start with 'coeff'
var percent = element;
var total = 0;
for (var i = 0; i < percent.length; i++) {
total += Number(percent[i].value);
}
return total == 100;
}, $.format("Percentage fields most total up to 100%"));
$("form").validate({
});
Updates
I've tried the following code with minor success
$("#modify-funding .percent").rules('add', {sum: 100});
$.validator.addMethod("sum", function (value, element, params) {
var sumOfVals = 0;
$("#modify-funding .percent").each(function () {
sumOfVals = sumOfVals + parseInt($(this).val().length ? $(this).val() : 0);
});
if (sumOfVals == params) return true;
return false;
},
$.format("Percentage fields most total up to 100%")
);
when passing the class into rules it's only passing in a single element which doesn't enable me to query for the additional percent fields. The other issue is it only adds the error class to the first .percent element and the additional elements will not release the error once the 100% criteria has been met.
Update 2
This is the closest I've come to getting percent validation working. It isn't very efficient code since it needs to loop through all the percent fields every time you loop the rules. The code still validates before a submit action has taken place plus does not clear all the percent errors on keyup when the percent has been corrected to equal 100.
$("#modify-funding .percent").each(function() {
$(this).rules('add', {sum: [".percent"]});
})
$.validator.addMethod("sum", function (value, element, params) {
var sumOfVals = 0;
$(params[0]).each(function() {
sumOfVals += parseFloat($(this).val().length ? $(this).val() : 0);
});
if (sumOfVals == 100) return true;
return false;
},
$.format("Percentage fields most total up to 100%")
);
Don't use jquery for such a simple validation. I don't really understand what all the hype is about jquery. It just makes your code look all ugly.
function validate() {
var ret = false;
var total = 0;
var elems = document.getElementsByTagName('*'), i;
for (i in elems)
if (elems[i].className == "percent")
total += parseFloat( elems[i].value);
if (total == 100)
ret = true;
return ret;
}