Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending jQuery Form Validation Script for new form fields

I have a simple HTML form that originally was a series of Questions (A1 to A5 and B1 to B3) with yes/no radio buttons like this:

<tr>
      <td width="88%" valign="top" class="field_name_left">A1</td>
      <td width="12%" valign="top" class="field_data">         
                    <input type="radio" name="CriteriaA1" value="Yes">Yes<input     type="radio" name="CriteriaA1" value="No">No</td>
</tr>

The user could only answer either the A series of questions OR either the B series of questions, but not both. Also they must complete all questions in either the A or B series.

I now have an additional series of questions - C1 to C6 - and need to extend my validation scripts to ensure the user enters either A, B or C and answers all questions within each series.

My original script for just the A and B looks like this:

    $(function() {

        $("#editRecord").submit(function(){

        // is anything checked?
        if(!checkEmpty()){
            $("#error").html("Please check something before submitting");
            //alert("nothing Checked");
            return false;
        }
        // Only A _OR_ B
        if(isAorB()){
            $("#error").html("Please complete A or B, not both");
            //alert("please complete A or B, not both");
            return false;
        };
        // all A's or all B's
        if(allAorBChecked()){
            $("#error").html("It appears you have not completed all questions");
            //alert("missing data");
            return false;
        };
        if(haveNo()){
            // we're going on, but sending "type = C"
        }
        //alert("all OK");
            return true;
        });
    });


function checkEmpty(){
    var OK = false;
    $(":radio").each(function(){
        if (this.checked){
            OK = true;
        }
    });
    return OK;
}
 function isAorB(){
    var OK = false;
    var Achecked = false;
    var Bchecked = false;
    $(":radio").each(function(){
        var theChar=this.name.charAt(8);
        // if we have an A checked remember it
         if(theChar == "A" && this.checked && !Achecked){
            Achecked = true;    
        }
        if(Achecked && theChar == "B" && !Bchecked){
            if(this.checked){
                Bchecked = true;
            }
        }
        if (Achecked && Bchecked){
            OK = true;
        }
    });
    return OK;
} 
function allAorBChecked(){
    var notOK = false;
    var Achecked = false;
    $(":radio").each(function(){
        // skip through to see if we're doing A's or B's
    var theChar=this.name.charAt(8);
        // check the A's
     if(theChar == "A" && this.checked && !Achecked){
            Achecked = true;    
        }

    });
    if(Achecked){
        // set the input to A
        $("#type").val("A");
        // check _all_ a's are checked
        var thisName;
        var thisChecked = false;

        $(":radio").each(function(){
            var theChar=this.name.charAt(8);
            var checked = this.checked;
            if (theChar == "A"){
            if (this.name == thisName && !thisChecked){
                // Yes wasn't checked - is No?
                if(!checked){
                    notOK = true;
                }
            }
            thisChecked = checked;
            thisName = this.name;
        }
    });
    }else{
        // set the input to B
        $("#type").val("B");            
        // check _all_ b's are checked
            var thisName;
            var thisChecked = false;
            $(":radio").each(function(){
                var theChar=this.name.charAt(8);
                var checked = this.checked;
                if (theChar == "B"){
                if (this.name == thisName && !thisChecked){
                    // A wasn't checked - is B?
                    if(!checked){
                        notOK = true;
                    }
                }
                thisChecked = checked;
                thisName = this.name;
            }
        });
    }
    return notOK;
}    
function haveNo(){
    var thisName;
    var notOK = false;
        $(":radio").each(function(){
            var checked = this.checked;
            if (this.name == thisName){
                //Is this checked 
                if(checked){
                    notOK = true;
                    $("#type").val("C");            
                }

        }
        thisName = this.name;
    });

    return notOK;
}

This worked well but I'm completely stuck at extending it to include the C series. I now have to check that the user hasn't answered any A and B, A and C and B and C questions. Everything I've tried fails to validate. Here's where I'm at right now with my new script:

    $(function() {

        $("#editRecord").submit(function(){

        // is anything checked?
        if(!checkEmpty()){
            $("#error").html("Please check something before submitting");
            //alert("nothing Checked");
            return false;
        }
        // Only A or B or C
        if(isAorBorC()){
            $("#error").html("Please complete A or B or C, not both");
            //alert("please complete A or B, not both");
            return false;
        };
        // all A's or all B's or all C's
        if(allAorBorCChecked()){
            $("#error").html("It appears you have not completed all questions");
            //alert("missing data");
            return false;
        };
        if(haveNo()){
            // we're going on, but sending "type = C"
        }
        //alert("all OK");
            return true;
        });
    });


function checkEmpty(){
    var OK = false;
    $(":radio").each(function(){
        if (this.checked){
            OK = true;
        }
    });
    return OK;
}
function isAorBorC(){
    var OK = false;
    var Achecked = false;
    var Bchecked = false;
    var Cchecked = false;
    $(":radio").each(function(){
        var theChar=this.name.charAt(8);
        // if we have an A checked remember it
         if(theChar == "A" && this.checked && !Achecked){
            Achecked = true;    
        }
        if(theChar == "B" && this.checked && !Achecked){
            Bchecked = true;    
        }
        if(theChar == "C" && this.checked && !Achecked){
            Cchecked = true;    
        }
        if(Achecked && theChar == "B" && !Bchecked){
            if(this.checked){
                Bchecked = true;
            }
        }
        if(Achecked && theChar == "C" && !Cchecked){
            if(this.checked){
                Cchecked = true;
            }
        }
        if(Bchecked && theChar == "C" && !Cchecked){
            if(this.checked){
                Cchecked = true;
            }
        }
        if (Achecked && Bchecked){
            OK = true;
        }
        if (Achecked && CBchecked){
            OK = true;
        }
        if (Bchecked && Cchecked){
            OK = true;
        }
    });
    return OK;
} 
function allAorBorCChecked(){
    var notOK = false;
    var Achecked = false;
    $(":radio").each(function(){
        // skip through to see if we're doing A's or B's
    var theChar=this.name.charAt(8);
        // check the A's
     if(theChar == "A" && this.checked && !Achecked){
            Achecked = true;    
        }

    });
    if(Achecked){
        // set the input to A
        $("#type").val("A");
        // check _all_ a's are checked
        var thisName;
        var thisChecked = false;

        $(":radio").each(function(){
            var theChar=this.name.charAt(8);
            var checked = this.checked;
            if (theChar == "A"){
            if (this.name == thisName && !thisChecked){
                // Yes wasn't checked - is No?
                if(!checked){
                    notOK = true;
                }
            }
            thisChecked = checked;
            thisName = this.name;
        }
    });
    }elseif{
    // set the input to B
        $("#type").val("B");            
        // check _all_ b's are checked
            var thisName;
            var thisChecked = false;
            $(":radio").each(function(){
                var theChar=this.name.charAt(8);
                var checked = this.checked;
                if (theChar == "B"){
                if (this.name == thisName && !thisChecked){
                    // A wasn't checked - is B?
                    if(!checked){
                        notOK = true;
                    }
                }
                thisChecked = checked;
                thisName = this.name;
            }
        });
    }
    return notOK;
}   

    }else{
        // set the input to C
        $("#type").val("C");            
        // check _all_ c's are checked
            var thisName;
            var thisChecked = false;
            $(":radio").each(function(){
                var theChar=this.name.charAt(8);
                var checked = this.checked;
                if (theChar == "C"){
                if (this.name == thisName && !thisChecked){
                    // A wasn't checked - is B?
                    if(!checked){
                        notOK = true;
                    }
                }
                thisChecked = checked;
                thisName = this.name;
            }
        });
    }
    return notOK;
}   
function haveNo(){
    var thisName;
    var notOK = false;
        $(":radio").each(function(){
            var checked = this.checked;
            if (this.name == thisName){
                //Is this checked 
                if(checked){
                    notOK = true;
                    $("#type").val("C");            
                }

        }
        thisName = this.name;
    });

    return notOK;
}

Anyone see what I'm doing wrong?

like image 551
user982124 Avatar asked Feb 23 '26 08:02

user982124


1 Answers

First of all, I think that the best usability would be to make the form easier to use, by making it harder to get wrong. If you are using javascript & jQuery, you may as well set it up so that the user has to pick a section before being able to select any items in it. You can easily support the requirement of showing all the questions, but it is a disservice to the users to allow them to perform needless work (such as filling out more than one section mistakenly) when the computer could very easily prevent this.

If you would like to see what that might look like, please comment and I will work something up for you.

In the meantime, going with your current design, here is a fiddle demonstrating one possible technique. It will work with any number of sections.

Please see that code for a full listing, but here is the script that does the heavy lifting of validation.

function validate(ev) {
    var sectionsTouched = $('ol').has(':checked'),
       inputs = {},
       errorMessage = '';

    if (sectionsTouched.length === 0) {
        errorMessage = 'Please check something before submitting.';
    } else if (sectionsTouched.length > 1) {
        errorMessage = 'Please complete only one of sections ' + namesCommaDelimited(seriesNames(), 'or') + '.';
    } else {
        sectionsTouched.find('input').each(function(i, e) {
            var me = $(e), name = me.attr('name');
            inputs[name] = !!inputs[name] || me.is(':checked');
        });
        $.each(inputs, function(k, v) {
            if (!v) {
                errorMessage = 'Please complete all questions in your selected section.';
            }
            return v;
        });            
    }
    if (errorMessage !== '') {
        $('#error').html(errorMessage);
        ev.preventDefault();
        return false;
    }
    return true;
}

Basic logic: if no section has a checked item, display the appropriate message. If more than one section has a checked item, too many sections have answers. If exactly one section has answers, loop through the inputs and collect a list of those that have checks for the corresponding control name. If any name exists that did not have a check, then some control group was left unchecked, and the appropriate message is displayed.

I had to take some liberties to construct a form that suits the requirements well enough for a proper demonstration. Here is what it looks like:

My demonstration fiddle

I hope this helps!

P.S. For the sake of completeness I would like to mention a couple of style considerations:

  • When you find yourself repeating code, try to abstract the repetition away--create a function, or declare a variable. For example, instead of doing $('#error').html() multiple times, set a value then do the html-setting only once.
  • Try to write code that doesn't secretly depend on document structure such as form field names being in a particular format (having a particular character at a position). Your javascript depends heavily on the charAt(8) function returning 'A' or 'B', and this makes it fragile--what if someone else (or even your future self) were to accidentally break the control names? Suddenly the javascript would break and the link between the two is not obvious. You can code it so you don't even need to know which section is which, as long as you have some selector that can indicate each individual section. Using a class name is okay because the class name in javascript obviously has to match the one in the html.
like image 115
ErikE Avatar answered Feb 24 '26 22:02

ErikE