Javascript help

Hello Experts,
I have a web form which has 2 sets of fields, the fields are referenced as individuals:
Section 1 has 4 fields A, B, C, D  these are drop down fields which contain numbers, these are considered as Price
Section 2 has 4 fields 1,2,3,4 these are type text where users can write numbers, these are considered Hours

There is a last field Total where the No. of hours multiplied with Price should be summed together.

The calculations have to be real time as the user is using these fields basically for checking how much to offer, so they can at anytime change the no. of hours or price to evaluate the amount.

Thanks a lot in advance!
A
LVL 11
AcklesAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
This can be done with this markup
HTML
    <table>
      <thead>
        <tr>
          <th>Price</th>
          <th>Hours</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>
            <select class="price input-item" name="price_1" id="price_1" data-link="hours_1">
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
            </select>
          </td>
          <td>
            <input type="text" class="hours input-item" name="hours_1" id="hours_1" data-link="price_1"/>
          </td>
          <td>
            <input type="text" class="total text-right  price_1 hours_1" name="total_1" readonly/>
          </td>
        </tr>
        <tr>
          <td>
            <select class="price input-item" name="price_2" id="price_2" data-link="hours_2">
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
            </select>
          </td>
          <td>
            <input type="text" class="hours input-item" name="hours_2" id="hours_2" data-link="price_2"/>
          </td>
          <td>
            <input type="text" class="total text-right  price_2 hours_2" name="total_2" readonly />
          </td>
        </tr>
        <tr>
          <td>
            <select class="price input-item" name="price_3" id="price_3" data-link="hours_3">
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
            </select>
          </td>
          <td>
            <input type="text" class="hours input-item" name="hours_3" id="hours_3" data-link="price_3"/>
          </td>
          <td>
            <input type="text" class="total text-right  price_3 hours_4" name="total_3" readonly />
          </td>
        </tr>
        <tr>
          <td>
            <select class="price input-item" name="price_4" id="price_4" data-link="hours_4">
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
            </select>
          </td>
          <td>
            <input type="text" class="hours input-item" name="hours_4" id="hours_4" data-link="price_4"/>
          </td>
          <td>
            <input type="text" class="total text-right  price_4 hours_4" name="total_4" readonly />
          </td>
        </tr>
      </tbody>
    </table>

Open in new window

And this jQuery
<script>
$(function() {
  $('.input-item').change(function() {
    var link = $(this).data('link');
    var total = $(this).val() * $('#' + link).val();
    $('.total.' + link).val(total);
    
  });
});
</script>

Open in new window

Working sample here
AcklesAuthor Commented:
Thanks, but there is no table, these are independent fields
Alexandre SimõesManager / Technology SpecialistCommented:
@Ackles it doesn't matter, just follow the same logic @Julian implemented to link the selects and the textboxes. The Javascript will work anyway.

The only thing that is missing there is to handle the value change of the select also.
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

Tom BeckCommented:
Another working example.

http://jsfiddle.net/dyn4578x/1/
Julian HansenCommented:
Thanks, but there is no table, these are independent fields
The table is irrelevant and is used only to layout the controls. The code will work irrespective of how you layout your controls.

The linking is done on classes and custom data attributes.
AcklesAuthor Commented:
Thanks, will try.
AcklesAuthor Commented:
I tried, seems I am too .... to even make it work;
Is it possible for you to write the syntax & I will change the references as per my CSS?

Hope it's not too much to ask....
Julian HansenCommented:
What more do you need apart from what I have already posted?

Is it possible for you to write the syntax & I will change the references as per my CSS
Your CSS should not have any bearing on this?

Maybe what you should do is post your markup for your controls and lets work from that.

Having said that the solution I posted  works like this
You have <select> elements that store price. The bits in bold are the bits that are needed for the solution
<select class="price input-item" name="price_1" id="price_1" data-link="hours_1">
1. the input-item class
The jQuery event handler is bound to all elements with this class - if any of them change it triggers and updates the corresponding totals box. All you need to do is add this class to your select (in addition to any other classes that are required for your styling). You can change this class name if you wish - but make sure you change it for all <select> and <input> pairs as well as in the jQuery.

2. the id value price_1 (for the first select, price_2 for the second) etc can be changed to what you want but then you need to change the data-link attribute in the partner element. NB id's must be unique

3. the data-link custom attribute specifies what element this is partnered to - it must equal the id (without the '#') of its partner item so select price_1 is linked to input hours_1

The input's are similar
<input type="text" class="hours input-item" name="hours_1" id="hours_1" data-link="price_1"/>
Same rules apply

Finally there are the total inputs
<input type="text" class="total price_1 hours_1 text-right" name="total_1" readonly/>
The total class allows us to target a total box.
I have added both classes for the corresponding partnered boxes so we can use either one to target the corresponding total

The JQuery
// make sure you include the jquery library here CDN of your choice
<script src="http://code.jquery.com/jquery.js"></script>
<script>
$(function() {
  // BIND EVENT HANDLER TO CHANGE EVENT
  // ON ALL ELEMENTS WITH THE CLASS input-item
  $('.input-item').change(function() {

    // FIND THE ID OF THE PARTNER ITEM
    // IT IS STORED IN THE data-link CUSTOM ATTR
    var link = $(this).data('link');

    // CALC THE TOTAL BASED ON THE val() OF 
    // THE ELEMENT THAT CHANGED MULT BY
    // THE VALUE OF ITS PARTNER ITEM WHICH
    // IS OBTAINED BY THE LINK VALUE PRE-PENDED
    // WITH A '#' TO TARGET THE ELEMENT WITH link ID
    var total = $(this).val() * $('#' + link).val();

    // FINALLY SET THE VALUE OF THE CORRESPONDING TOTAL
    // THIS FINDS THE ELEMENT WITH CLASS .total APPENDED
    // WITH THE link VALUE. BECAUSE THE TOTAL ELEMENT HAS
    // BOTH LINK VALUES (AS CLASSES) FOR THE PAIRED ITEMS USED
    // TO DERIVE ITS VALUE - USING THE LINK VALUE FROM EITHER WILL
    // TARGET THE CORRECT .total ELEMENT
    $('.total.' + link).val(total);
  });
});
</script>

Open in new window

No dependence on structure and can be added to existing markup without changes to CSS. If class names conflict with any of your existing class names change them in line with the rules above.
AcklesAuthor Commented:
Thanks for the detailed explanation, really appreciate !

Please excuse my being naive, as I have no experience with it.... the software help files of the one I have to write for says that to capture the input value of a field the syntax is: Targets the field's input box. Useful when dealing with a field's value -      .$('#q1 input').val();

I am not using any class & intend to directly capture the value by field id

Now, I have four fields (Rate) which are dropdown values & their ID corresponding to #q1 as per above is:
#q141, #q142, #q143 & #q165

The fields where user gives no. of hours have the id's:
#q159, #q160, #q161 & #q166

The result after the hours multiplied with rate should give total in field with id: #q157

It would be very kind if you could tweak the code as per it.

Regards,
A
Julian HansenCommented:
In jQuery there are many ways to skin a cat. The following code - the input is superfluous - ID's are unique so this
$('#q1 input').val()

Open in new window

Can be done like this
$('#q1').val()

Open in new window

Rather post your markup and lets work from that - the instructions I posted earlier can be applied to pretty much any code but rather than try and modify them based on a loose description of the target markup is not really going to be efficient. Post your markup and lets work from that.
AcklesAuthor Commented:
I will tomorrow, I'm off work now, thanks
But in reality I haven't written anything yet
Julian HansenCommented:
But in reality I haven't written anything yet
Then I am confused by why the solution above does not work for you - if you have not coded the form then coding it based on the outline above will get you where you want to go.
AcklesAuthor Commented:
Sorry, I never meant that it doesn't work.
I. Meant I don't know how to translate it as per my id,s
Julian HansenCommented:
Take a look at this sample (http://www.marcorpsa.com/ee/t1179a.html)
This uses the same process - all that I have done is used bootstrap columns instead of tables and changed the id's to the ones you mentioned above (q141, q142) etc.
As you will see in the jQuery - it does not care what the id's and classes are - only the structure matters so as long as each select refers to the correct input through the data-link attribute and vice versa and both are linked to the total with the link values as classes to the total then the solution works.
The disadvantage with your naming system over the previous sample is that with the previous sample prices_1, hours_1, total_1, prices_2, hours_2 etc - it is very easy to see what belongs to what - but it really does not matter what id's you use - as long as they are unique and the classes and data-link values are setup correctly.
AcklesAuthor Commented:
Thanks Julian,
Will try tomorrow & report.
I'm in Europe & it's night here, but I promise too report back.
What I understand is that you have given all the fields a class input & then called it in function. Correct?
Julian HansenCommented:
I'm in Europe & it's night here, but I promise too report back.
NP - I am in Africa and it is just as dark ....

What I understand is that you have given all the fields a class input & then called it in function.
Ummm - sort of.
The principle is that each group consists of 3 elements.

A pair of select / inputs - these are linked by their id's - the one's id is set in the others data-link attribute and vice-versa

A single total field - which is linked to both using their ids (or data-link attributes) as classes.

That gives us a three way linkage.

The final bit that makes it work is a common class (input-item) that is common to all select / input pairs.

What we do is set up an event handler to look for changes to any element that has the input-item class.

What is an event handler? When something happens on a page - like a click or a mouse event or a keypress - anything that would means something happened (an event) - the browser notifies any process that has asked to be notified about the event.

the code $('.input-item').change(function() { ... }) is a way of saying to the browser - whenever you detect a change in value on any element that has the input-item class - then run this function - we supply the function as a parameter to the event handler registration.

The browser now attaches our function to the change event of all elements with the input-item class. That's the hard part done - we don't have to do anything until something happens in one of those input controls.

When it does we use the information that is stored with the control to figure out what to do.
We use the data-link of the changed item to find out who its partner is - once we have that it is a simple case of using the $('#q...').val() you read about to get the two values and multiply them.

Now we have the total - all we need to do is put it somewhere - so how to find the right total element? Well we know it has a class .total - but so do 3 other total inputs - so how to distinguish them - that's easily done -because each total input has two classes that are identical in name to the id's of the elements it is linked to - we only need one of those id's to combine with the .total class to create a selector to uniquely identify the total input we want to set. It would look something like
$('.total.q141').val(total) which says to jQuery - find me all elements that have both the class .total AND q141 and for each one you find set its value to total

If you have set everything up correctly there will be only one element that matches those criteria - job done
AcklesAuthor Commented:
Alright Julian,
I have all the fields which are price: #q141, #q142, #q143 & #q165
 I give them class Price

then all the fields which are hours: #q159, #q160, #q161 & #q166
I give them class Hours

To the field #q157 I give class Total


Now how would you manipulate the function as this is the only part I need to work with

$(function() {
  $('.input-item').change(function() {
    var link = $(this).data('link');
    var total = $(this).val() * $('#' + link).val();
    $('.total.' + link).val(total);
   
  });
});

I am really sorry, but maybe I am too stressed so can't get my head around....
Julian HansenCommented:
You don't need to change the function - it is generic.

select1: class: input-item id: SELECT_ID_1 data-link: INPUT_ID_1
input1: class input_item id: INPUT_ID_1 data-link: SELECT_ID_1
total1: class total INPUT_ID_1 SELECT_ID_1

select2: class: input-item id: SELECT_ID_2 data-link: INPUT_ID_2
input2: class input_item id: INPUT_ID_2 data-link: SELECT_ID_2
total2: class total INPUT_ID_2 SELECT_ID_2
...

Open in new window


Change the CAPS items to whatever you want but make sure that the matching CAPS items stay matched after the change and are unique outside of their pairing.

That's all you have to do - the function will do its thing without any changes.
Julian HansenCommented:
Ackles said: (email)
what I am asking is the end result of the code, the software I am using is called Laserfiche.
Below is the URL where it’s described how the generic code looks like:

https://answers.laserfiche.com/questions/47631/How-do-I-perform-calculations-on-fields-in-Forms


I am totally naive in terms of scripting & it’s hard for me to put head around the code.

As I mentioned in my last post that I can give 3 classes depending upon 3 types of fields I have, now would it be possible for you to help me with the end code as described in the example in the URL I pasted?
That link is to a forum post but it has a link to the LaserFiche forms example - you should have mentioned that you were using a form generator.

The sample that is described talks about how to add a number of fields together - very simple give them all the same class and sum all fields with that class.

That is not what you are doing - you are pairing a drop down with an input and putting the product of their values in a third input - and you are doing that 4x.

Having said that I have put another sample together that works off only 3 classes per group. You can view it here
A group of items now looks like this
  <div class="row">
    <div class="col-xs-12">
      <select class="input-item group_1" name="q141" >
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
      </select>
      <input type="text" class="input-item group_1" name="q159"/>
      <input type="text" class="total group_1 text-right" name="total_1" readonly/>
    </div>
  </div>

Open in new window

input-item is still used to identify price and hours inputs
group_? is common to all inputs in a group - but different across groups. Group 1 inputs all have group_1, Group 2 inputs all have group_2 etc
total identifies total inputs - which also have the group_? class
No need for id's or data-link attributes
jQuery
<script>
$(function() {
  // SAME AS BEFORE - TRIGGER ON change EVENT
  $('.input-item').change(function() {

  // DIFFERENT - FIND THE group_? CLASS FOR THIS ITEM
  // WE NEED IT TO FIND THE OTHER INPUT AND THE TOTAL
  // USE A REGULAR EXPRESSION ON THE class ATTRIBUTE
  // TO FIND A GROUP THAT STARTS group_ WITH A SINGLE
  // DIGIT ON THE END
  var classitem = $(this).attr('class').match(/(group_\d)/)[1];

  // CALC THE TOTAL USING THE group_? CLASS COMBINED
  // WITH THE input-item CLASS AND THE ELEMENT TYPE
  var total = parseNumber($('select.input-item.' + classitem).val()) * parseNumber($('input.input-item.' + classitem).val());

    // SAME AS BEFORE
    $('.total.' + classitem).val(total);
    
  });
});
// FUNCTION TO SAFELY PARSE NUMBER
function parseNumber(n)
{
  var f = parseFloat(n);
  return isNaN(f) ? 0 : f;
}
</script>

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AcklesAuthor Commented:
Awesome!!
Thanks!
AcklesAuthor Commented:
So, I came up with this code , but unfortunately it gives 0 in total field:

$(document).ready(function() {
  
  // Hide the appropriate Fields when form first loads
  $('#q164,#q163, #q87, #q74').hide();
  
  
    $('.cf-section-block').on('blur', 'input', compute);
    $('.cf-section-block').on('blur', 'select', compute);
    
     function compute() {
          var a = parseNumber($('#q141 input').val());
          var b = parseNumber($('#q142 input').val());
          var c = parseNumber($('#q143 input').val());
          var d = parseNumber($('#q165 input').val());
          var x = parseNumber($('#q159 select').val());
          var y = parseNumber($('#q160 select').val());
          var z = parseNumber($('#q161 select').val());
          var q = parseNumber($('#q166 select').val());
 
 
          var total = (a*x) + (b*y) + (c*z) + (d*q);
          $('#q157 input').val(total);
    }
         
     
     function parseNumber(n) {
        var f = parseFloat(n); //Convert to float number.
       return isNaN(f) ? 0 : f; //treat invalid input as 0;
    }
    
     
   
});

Open in new window

Julian HansenCommented:
Just as a matter of interest - what was wrong with the code I posted?
AcklesAuthor Commented:
Apologies, Nothing was wrong.... sorry if it meant as some offence.

This seemed to be more clear, I am sure this is not as refined as yours, but was easy for me to see....
Again, nothing wrong with your code or explanation, it's just that this is my first time. ....

Can you suggest why it's giving 0?
Julian HansenCommented:
Not really - we would need a context.

1. Make sure the event is firing - put an alert('Some Message'); in the function to make sure it is being called

2. Put console.log(a) statements after each assignment console.log(b) etc and view in console to make sure they are being set correctly.

3. Make sure your ID's exist and are unique.

I see you are putting the totals all into one total box - just curious given the number of posts and samples that have dealt with a total per row why this was never clarified?
Julian HansenCommented:
Actually belay that
var a = parseNumber($('#q141 input').val());

Open in new window

Look at your selector

#q141 input

What you are saying is find the element with an id of #q141 and then find a child input of that element.

What you probably meant was
var a = parseNumber($('#q141').val());

Open in new window

Same for the rest of them.
AcklesAuthor Commented:
I've requested that this question be closed as follows:

Accepted answer: 0 points for Ackles's comment #a41095464

for the following reason:

Julian,
After reading your explanations again & again I figured it out, I had to give the type to select & input & it works like a charm!!!

Thanks a Million for your kind help, patience & support!!!!!!
AcklesAuthor Commented:
Julian,
After reading your description again, again & again I realised I had to give input & select for field types.
It works now like a charm!

Thanks a Million for your Support, help & patience!!!!

A
AcklesAuthor Commented:
This was it:

function compute() {
          var a = parseNumber($('#q141 select').val());
          var b = parseNumber($('#q142 select').val());
          var c = parseNumber($('#q143 select').val());
          var d = parseNumber($('#q165 select').val());
          var x = parseNumber($('#q159 input').val());
          var y = parseNumber($('#q160 input').val());
          var z = parseNumber($('#q161 input').val());
          var q = parseNumber($('#q166 input').val());
 
 
          var total = (a*x) + (b*y) + (c*z) + (d*q);
          $('#q157 input').val(total);
    }
         
     
     function parseNumber(n) {
        var f = parseFloat(n); //Convert to float number.
       return isNaN(f) ? 0 : f; //treat invalid input as 0;
    }
Julian HansenCommented:
You are welcome - good luck with your project.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.