2
\$\begingroup\$

I've got a form, for which the "Send" button should only be available, upon each form field being validated. For most of my check, I call a function checkFormValue() which is a function gathering all the simple checks, give warning where check failed, and if at least one of the check fail, disable the button. The call are made on the blur event for each field of the form.

function checkFormValue(){
    var form = document.myform;

    //initializing a few variable with check, amongst them the following
    //Those are example of check which are pure javascript function
    var checkIDField = checkField(form.myids) && checkIDs();
        //...

    //This is an example of AJAX function
    if(checkIDField){
        checkIDValidity();
    }

    //Checks variables are gathered under one variable
    var validform = checkIDField && CheckContactField && CheckPathsField;

    var warnings = document.getElementsByClassName('warning');
    var i;
    var count = 0;

    //Loop to check the number of warnings
    for (i = 0; i < warnings.length; i++){   
        // Check that current character is number.
        if(warnings[i].style.display===""){
            count++    
        }
    }
    validform = validform && (count === 0);
    document.form.send.disabled = !(validform);
}

A few check needs to be done on the server (SELECT query, check if some file exists,etc.). For those check,, I do AJAX calls, and I added in the checkFormValue() a loop which count the number of warning displayed. If none are displayed, then the button is not disable anymore.

One of my field should contain a series of ID, in the form of a space separated list. After checking that the list is correctly formatted (list of integer separated by space, no duplicate), I want to see if those ID exist in the database. The following function set the AJAX call:

function checkIDValidity(){
    var xmlhttp;
    if (window.XMLHttpRequest){ 
        //code for IE7+, Firefox, Chrome, Opera, Safari 
        xmlhttp=new XMLHttpRequest(); 
    }else{ 
        // code for IE6, IE5 
        xmlhttp=new ActiveXObject('Microsoft.XMLHTTP'); 
    }
    xmlhttp.onreadystatechange=function(){ 
        if (xmlhttp.readyState===4 && xmlhttp.status===200){ 
            var existingIDs = xmlhttp.responseText;
            var idcomponent = document.myform.myids;
            var idwarningdiv = document.getElementById('nonexistingid');
            if(existingIDs==="Existing"){
                idcomponent.setAttribute('class', 'valid');
                if(idwarningdiv.style.display !== 'none'){
                    idwarningdiv.style.display='none';
                    idwarningdiv.innerHTML='';
                }
            }else{
                idcomponent.setAttribute('class', 'invalid');
                if(idwarningdiv.style.display !== ''){
                    idwarningdiv.style.display='';
                }
                if(idwarningdiv.innerHTML.substr(51,existingIDs.length)!==existingIDs){
                    idwarningdiv.innerHTML='<br /><img src="img/warning2.png" alt="Warning!" />'+existingIDs+' is not an existing id.';
                }
            } 
        } 
    };
    var idnumbers = document.myform.myids.value;
    var d=new Date();
    //d.toUTCString() is used so the result never get cached
    xmlhttp.open('GET', 'checkidvalidity.php?id='+idnumbers+'&rand='+d.toUTCString(), true); 
    xmlhttp.send();
}

When I enter an ID, which doesn't exist in the database, I got a warning, and if the ID is valid, the warning disappear. But the enabled/disabled status of the "Send button" is not immediately updated, on the contrary, I need to click twice on other field in the form before I can see any change on the button.

Adding a listener on the warning div for non existent IDs, triggering on propertyChanged event and calling checkFormValue did at first the trick, but the event is only supported on IE, and as such it didn't work on other browser. Using the mutation event "DOMAttrModified" when it was supported, helped for Firefox and Opera.

function formLink(objectid){
    var xmlhttp;
    if (window.XMLHttpRequest){ 
        //code for IE7+, Firefox, Chrome, Opera, Safari 
        xmlhttp=new XMLHttpRequest(); 
    }else{ 
        // code for IE6, IE5 
        xmlhttp=new ActiveXObject('Microsoft.XMLHTTP'); 
    }
    xmlhttp.onreadystatechange=function(){ 
        if (xmlhttp.readyState===4 && xmlhttp.status===200){ 
            document.getElementById('terminal').innerHTML=xmlhttp.responseText;
            if(isDOMAttrModifiedSupported()){
                document.getElementById("nonexistingid").addEventListener("DOMAttrModified",
                  checkFormValue,
                  false);
            }
            checkFormValue(); 
        } 
    };
    xmlhttp.open('GET', 'myform.php?id='+objectid, true); 
    xmlhttp.send(); 
}

Details on the isDomAttrModifiedSupported function can be found on this post about detecting if the DomAttrModified event listener is supported.

But using that approach leaves my with no solution for the WebKit browsers (Chrome, Safari). Is there a way to refactor my code so I can do my AJAX call, and it would result on an update of the disabled status of the "Send" button, without discounting the result of my other simple checks? In other word, is my validation approach correct?

PS: I get some restraints on this project, and I am not allowed to use jQuery.

\$\endgroup\$
2
\$\begingroup\$

I would approach it slightly differntly.

Have functions to change when the field has been validated or not:

function checkFormValues(){

    var invalidFields = 0;

    function updateValidFields(isValidField)
    {

        invalidFields += isValidField ? -1 : 1;
        if(!invalidFields)
        {
             // Enable Submit Button
             // ???
             // Profit
        }
        else
        {
            // Disable submit Button
            // Fail!
        }
    }

    // etc
}

then your validation functions look something like:

function validateMe()
{
   //Always assume invalid to startwith
   updateValidFields(false);

   // do awesome field checking.

   if(thisFieldIsValid)
   {
        updateValidFields(true);
   }
}

then your checkValidID function would look like:

function checkIDValidity(){
    updateValidFields(false);

and the readyState change function:

xmlhttp.onreadystatechange=function(){ 
        if (xmlhttp.readyState===4 && xmlhttp.status===200){ 
            var existingIDs = xmlhttp.responseText;
            var idcomponent = document.myform.myids;
            var idwarningdiv = document.getElementById('nonexistingid');
            if(existingIDs==="Existing"){
                // Do valid stuff
                updateValidFields(true);
            }else{
                //do invalid stuff.
            } 
        } 
    };
\$\endgroup\$
  • \$\begingroup\$ Didn't you mean for invalidField to be a global variable, as well as for updateValidField() to be a publich function? \$\endgroup\$ – Eldros Sep 13 '11 at 6:55
  • \$\begingroup\$ @Eldros Nope. This way it creates the variable and function in the same scope and a new one each time. You coould make it Global if you wanted but i think its better this way as each new time it starts fresh. [yoda] Think it is a matter of preference I do.[/yoda] \$\endgroup\$ – James Khoury Sep 13 '11 at 23:36
  • \$\begingroup\$ Then I don't understand why the variable is in checkIDValidity, I mean it is only one check for one field (where there could be already many check on a field). It might be that I still think as a PHP developer, but wouldn't it be better to put it in the validateMe()(Which I take is your form validation function)? \$\endgroup\$ – Eldros Sep 14 '11 at 6:15
  • \$\begingroup\$ @eldros Sorry That first one was supposed to be CheckFormValue. oops! Fixed now. Then validateme would either check its validity for required and if that pass it would then check its ID (if needed). \$\endgroup\$ – James Khoury Sep 14 '11 at 6:34
  • 1
    \$\begingroup\$ Now, with a few adaptation from my side, it works perfectly. I may edit my post with the refactored code at a later time. \$\endgroup\$ – Eldros Oct 10 '11 at 11:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.