Mark Hill
Mark Hill

Reputation: 1839

Looping through function causes error and fails on first change

I have here the following script which is causing me some errors:

var sections = ["#general_info", "#address_records", "#employment_history", "#driver_experience", "#military_experience", "#eeo_survey", "#psp_notice", "#eva"];
for(var i = 0; i < sections.length; i++){ 
    $(sections[i]).find('input, select').each(function(){
        $(this).change(function(){
            validate();
            $(this).closest('.placement').find('.module-heading').removeClass('module-heading-bad');
            $(this).closest('.placement').find('.glyphicon').addClass('glyphicon-ok text-success');
            $(this).closest('.placement').find('.glyphicon').removeClass('glyphicon-warning-sign text-danger');
            $(sections[i]).find('input, select').each(function(){
                if($(this).closest('div').hasClass('has-error')){
                    $(this).closest('.placement').find('.module-heading').addClass('module-heading-bad');
                    $(this).closest('.placement').find('.glyphicon').addClass('glyphicon-warning-sign text-danger');
                    $(this).closest('.placement').find('.glyphicon').removeClass('glyphicon-ok text-success');
                    return false;
                }
            });
        });
    });
}

function validate(){
    var driving_qs = ['driving_exp_qa', 'driving_exp_qb', 'driving_exp_qc', 'driving_exp_qd'];
    for( var i = 0; i < driving_qs.length; i++){
        if($('input[name='+driving_qs[i]+']:checked').val()){
            $('input[name='+driving_qs[i]+']').closest('.form-group').removeClass('has-error');
            $('input[name='+driving_qs[i]+']').closest('.form-group').addClass('has-success');
        }else{
            $('input[name='+driving_qs[i]+']').closest('.form-group').addClass('has-error');
            $('input[name='+driving_qs[i]+']').closest('.form-group').removeClass('has-success');
        }
    }
    var fields = [{
        selector: $('.not-empty'),
        validations: [ isNotEmpty]
    },{
        selector: $('.email'),
        validations: [ isNotEmpty, isEmail]
    },{
        selector: $('.number'),
        validations: [ isNotEmpty, isNumber]
    },{
        selector: $('.number-noreq'),
        validations: [isNumberNotRequired]
    }];
    $('.form-control').closest('div').removeClass('has-error');
    var i = 0, k = 0, z = 0, j = fields.length, item, selector, fn, info;
    for(; i < j; i++){
        item = fields[i];
        for(k = 0; k < item.validations.length; k++){
            fn = item.validations[k];
            for( z = 0; z < item.selector.length; z++){
                selector = $(item.selector[z]);
                info = selector.closest('div');
                if(info)
                    var result = fn(selector.val());
                if(result){
                    info.removeClass("has-error");
                    info.addClass('has-success');
                }else{
                    info.removeClass('has-success');
                    info.addClass("has-error")
                }
            }
        }
    }
}

The script works perfectly fine if I am running it without the for loop in front of it. Here is a quick step by step of what my code does (note: this is without the for loop):

  1. Locate the section in code and find each input an select field
  2. Assign the change event to each target input and select field
  3. On change find closest span of class placement, and fine the first module heading and perform all the adding and removing of classes, just to refresh the heading to a success heading if no errors exist below.
  4. Find all the inputs and selects and check for errors, if they exists return false, and add the error classes back on everything

This script will work all the way to the end of each section like it is supposed to do. However after I tried to do this with the for loop, it created a success scenario after only one input. Why is this happening, and is it even possible to have this function inside a loop like I am currently doing?

Also below I have included samples of the html mark-up

<!-- this tag serves no purpose other than being a bookmark for scripting -->
<span class='placement'>
    <!-- Section 1: General Information  -->
    <div id='general-heading' class='row module-heading module-heading-bad general' data-toggle='#general_info'>
        <div class='form-group'>
            <div class='col-md-12'>
                <h4 class='text-info '>General Information<div id='general-confirmation' class='glyphicon glyphicon-warning-sign pull-right text-danger'></div></h4>    
            </div>
        </div>
    </div>
    <div id='general_info' class='app-section'>
        <div class='form-group'>
            <div class='col-xs-12'>
                <div class='form-group row'>
                    <div class='col-sm-6 col-xs-12'>    
                        <label class='control-label'>First Name<span class='req'> *</span></label><br />
                        <input type='text' class='form-control not-empty' id='first_name' value="<?=$first_name?>"/>
                    </div>
                    <div class='col-sm-6 col-xs-12'>
                        <label class='control-label'>Middle Name</label><br />
                        <input type='text' class='form-control' id='middle_name' value="<?=$middle_name?>"/>
                    </div>
                </div>
            </div>
        </div>
</span>

Upvotes: 2

Views: 89

Answers (1)

parchment
parchment

Reputation: 4002

The problem in this block of code:

for(var i = 0; i < sections.length; i++){ 
    $(sections[i]).find('input, select').each(function(){
        $(this).change(function(){
            ...
            $(sections[i]).find('input, select').each(function(){
                ...
                }
            });
        });
    });
}

Is that it uses the variable i, which will have changed when the function() inside change is run.

In your case, the simplest way to fix it would be by using the forEach function instead of a for loop, and not using the index at all:

sections.forEach(function(section){ 
    $(section).find('input, select').each(function(){
        $(this).change(function(){
            ...
            $(section).find('input, select').each(function(){
                ...
                }
            });
        });
    });
})

This will ensure that the i you mean is different each time.

Upvotes: 1

Related Questions