Link to home
Start Free TrialLog in
Avatar of Ackles
AcklesFlag for Switzerland

asked on

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
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

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
Avatar of Ackles

ASKER

Thanks, but there is no table, these are independent fields
@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.
Another working example.

http://jsfiddle.net/dyn4578x/1/
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.
Avatar of Ackles

ASKER

Thanks, will try.
Avatar of Ackles

ASKER

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....
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.
Avatar of Ackles

ASKER

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
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.
Avatar of Ackles

ASKER

I will tomorrow, I'm off work now, thanks
But in reality I haven't written anything yet
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.
Avatar of Ackles

ASKER

Sorry, I never meant that it doesn't work.
I. Meant I don't know how to translate it as per my id,s
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.
Avatar of Ackles

ASKER

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?
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
Avatar of Ackles

ASKER

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....
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.
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Ackles

ASKER

Awesome!!
Thanks!
Avatar of Ackles

ASKER

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

Just as a matter of interest - what was wrong with the code I posted?
Avatar of Ackles

ASKER

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?
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?
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.
Avatar of Ackles

ASKER

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!!!!!!
Avatar of Ackles

ASKER

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
Avatar of Ackles

ASKER

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;
    }
You are welcome - good luck with your project.