0

I am using jquery to duplicate several html fields based on user's selection. However, I met an interesting problem. In general, I am asking users to select how many applications they want:

  1. if there is only one application: a. One need to choose application method (for simplicity, only 'aerial' is available); b. after selecting 'aerial', it will ask you for the further information, chemically application method (CAM).

  2. if they choose two applications, jquery code will clone and rename the necessary questions for you.

My problem is when I choose there are two applications, the sub-question 'CAM' will not show up. After some trouble shoot, I found the problem could be in this javascript :$('.app_method:last').find('select').change(function(). The statement, automatically increase my loop index by one (Can anyone tell me why this will happen?), which mismatch the code.

Here is a DEMO for my code:

Below is my html code:

<div class="articles">
        <table align="center">

        <tr><th><label for="id_NOA">Number of applications:</label></th><td><select name="NOA" id="id_NOA">
            <option value="1" selected="selected">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select></td></tr>

        <tr><th><label for="id_Ap_m">Application method 1</label></th><td><select name="Ap_m" id="id_Ap_m">
            <option value="" selected="selected">Select an application method</option>
            <option value="1">Aerial</option>
        </select></td></tr>

        <tr><th><label for="id_CAM_1">Chemical application Method (CAM) 1</label></th><td><select name="CAM_1" id="id_CAM_1">
            <option value="2">2-Interception based on crop canopy</option>
            <option value="9">9-Linear foliar based on crop canop</option>
        </select></td></tr>
        </table>
 </div>

​ jquery code:

$(document).ready(function() {

    //this part of code controls inputs when there is only one application
    $('#id_CAM_1').attr('id', 'id_1').closest('tr').addClass('method_options').hide();
    $('#id_Ap_m').change(function() {
        $('tr.method_options').hide();
        if ($(this).val() == "1") {
            $('#id_' + $(this).val()).closest('tr').show();
        }
    });

    i = 1;
    $('.articles').find('table').addClass('table');
    $('#id_Ap_m').closest('tr').addClass('app_method');

    $('#id_NOA').change(function() {
        var total = $(this).val();

        //remove all
        $('.app_method').each(function(index) {
            if (index != 0) $(this).remove()
        });

        //create new ones
        for (var i = 2; i <= total; i++) {
            alert('a=' + i);

            $('.app_method:first').clone().appendTo('.table').find('label').text('Application method ' + i);
            $('.app_method:last').find('select').attr('name', 'Ap_m' + i).attr('id', 'id_Ap_m' + i);
            alert('b=' + i);

            $('<tr class="method_options_1' + i + '" style="display: none;"><th><label for="id_CAM_1">Chemical application Method (CAM)' + i + '</label></th><td><select name="CAM_1_' + i + '" id="id_1_' + i + '"><option value="2">2-Interception based on crop canopy</option><option value="9">9-Linear foliar based on crop canop</option></select></td></tr>').appendTo('.table');
            alert('c=' + i);

//The following statement increase the loop index by one, which causes me problems. Can    
//anyone let me know why this could happen?
            $('.app_method:last').find('select').change(function() {
                alert('d=' + i)
                $('.method_options_1').hide();
                alert('e=' + i);

                if ($(this).val() == "1") {
                    alert('e=' + i);
                    $('.method_options_1' + i).show();
                }
            })
        }
    })
})​

2
  • Since you're not re-assigning to i or total, this does not seem to be the case. What makes you think the loop index is changing? Does the code block in your loop run the required number of times? This is easily confirmed by putting a console.log() as the first line in your loop. Commented Jun 29, 2012 at 22:51
  • @Utkanos, I put some alerts to show the change of i, and happen found within one loop, the change() function automatically increase the i by one. Commented Jun 29, 2012 at 23:41

1 Answer 1

2

I think this can be done much more simply: (fiddle: http://jsfiddle.net/QaHWz/)

<!DOCTYPE html><html><head></head><body>

<table align="center"><tbody>
    <tr><th></th><td><a href="#" id="add_application">Add Application</a></td></tr>
</tbody></table>

<table id="template" style="display:none"><tbody>
    <tr>
        <th><label for="id_Ap_m_{n}">Application method {n}</label></th>
        <td>
            <select class="Ap_m" name="Ap_m_{n}" id="id_Ap_m_{n}" data-application="{n}">
                <option value="" selected="selected">Select an application method</option>
                <option value="1">Aerial</option>
            </select>
        </td></tr>
    <tr style="display:none" class="app_{n} method_1"><th><label for="id_CAM_{n}">Chemical Application Method (CAM) {n}</label></th><td><select name="CAM_{n}" id="id_CAM_{n}">
        <option value="2">2-Interception based on crop canopy</option>
        <option value="9">9-Linear foliar based on crop canopy</option>
    </select></td></tr>
</tbody></table>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
jQuery(function($) {
    var applications = 0;
    $('#add_application').click(function() {
        applications++;
        var last_row = $(this).closest('tr');
        last_row.before(jQuery('#template tbody').html().replace(/{n}/g, applications));
    });
    $(document).delegate('.Ap_m', 'change', function() {
        var app = $(this).data('application');
        $('.app_'+app).hide();
        $('.app_'+app+'.method_'+this.value).show();
    });
});
</script>
</body></html>

EDIT: The problem you are having with .change() is that you are using the i variable , which gets incremented before the function is run. You need to get the value of i into the function another way. Here is one possible way you can do it:

$('.app_method:last').find('select').bind('change', { row: i }, function(event) {
    var i = event.data.row;
    alert('d=' + i)
    // ...
});

The { row: i } bit causes jQuery to attach this data to the event object which is passed to the function. Then I create var i inside the scope of the function, which will not be affected by the i outside, and assign this value to it.

Sign up to request clarification or add additional context in comments.

14 Comments

Thanks for providing the solution. But can you let me know why my code does not work? It seems like the problem happened in the change() function.
There can be only one element per page with a certain ID; $('#id_Ap_m').change() refers only to the first element with the ID "id_Ap_m"
I just noticed that my "solution" does not do what you wanted... editing now.
It does not increase your loop index. After the loop completes, i is equal to total+1. This callback runs after the loop has finished running, and i is still equal to total+1. No matter now many times the callback runs, i=total+1
This demonstrates why the loop index is total+1 in the callback: jsfiddle.net/sv4MT
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.