REQ: Question on how to call functions (New JavaScript Student)

Discussion in 'Javascript' started by Sue, Dec 13, 2003.

  1. Sue

    Sue Guest

    After finishing up my first quarter JavaScript on 12/12/03, I decided
    to improve character checking on my project. In my project I only had
    to do very basic validation. Therefore, I only had one function to
    verify the name fields, age, email and gender.

    My question is: if I create a function for each field like the code
    below, what would be the best way to organize the functions and call
    them? Would I need one main function and place all other function
    inside that function with one call or have separate function and a
    separate call for each function.

    Remember, please keep you explanation simple the only JavaScript
    training I have had is less than three months in an online class where
    I might add the book did not mention things like lastindexOf(),
    charAt() and some other things that I was told to try.

    Sue


    function validate(FirstName) {
    var Characters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIGJLMNOPQRSTUVWXYZ"
    var ok = "yes";
    var temp;
    for (var i=0; i<FirstName.value.length; i++) {
    temp = "" + FirstName.value.substring(i, i+1);
    if (Characters.indexOf(temp) == "-1") ok = "no";
    }
    if (ok == "no") {
    alert("Please Enter a valid FirstName!");
    document.Register.FirstName.value="";
    document.Register.FirstName.focus();
    }
    }
     
    Sue, Dec 13, 2003
    #1
    1. Advertising

  2. Sue wrote on 13 Dec 2003 at Sat, 13 Dec 2003 22:25:33 GMT:

    <snip>

    > My question is: if I create a function for each field like the
    > code below, what would be the best way to organize the functions
    > and call them? Would I need one main function and place all
    > other function inside that function with one call or have
    > separate function and a separate call for each function.


    If the validation is /exactly/ the same, you can use the same
    function passing, as an argument, the object to be validated. If
    different fields need different validation techniques, use separate
    functions.

    <snip>

    > function validate(FirstName) {
    > var Characters =
    > "abcdefghijklmnopqrstuvwxyzABCDEFGHIGJLMNOPQRSTUVWXYZ"


    You missed the semi-colon (;).

    > var ok = "yes";


    Use a boolean here. There are two reasons:

    1) It's more efficient, in terms of both storage and speed (when
    comparing)
    2) It's what booleans were made for

    var ok = true;

    This means that you could then do:

    if (ok) {
    // field validates
    } else {
    // field has invalid characters
    }

    > var temp;
    > for (var i=0; i<FirstName.value.length; i++) {
    > temp = "" + FirstName.value.substring(i, i+1);


    Unless you omitted the contents for this example, remove the quotes.
    They do nothing here (String.substring() returns a String object).

    > if (Characters.indexOf(temp) == "-1") ok = "no";


    String.indexOf() returns an integer, not a string. The line above
    should read (continuing with the boolean idea):

    if (-1 == Characters.indexOf(temp)) ok = false;

    I also advocate placing constants first in comparisons. It can
    prevent errors resulting from typos:

    if( something = -1 )

    ...is perfectly valid, but probably wrong. This, on the other hand,
    is invalid and easy (or easier) to track down:

    if( -1 = something )

    > }
    > if (ok == "no") {


    With the changes to a boolean variable, this comparison can read:

    if (!ok) {
    // If not ok, alert the user
    }

    > alert("Please Enter a valid FirstName!");


    You should qualify that method call properly:

    window.alert(...);

    > document.Register.FirstName.value="";
    > document.Register.FirstName.focus();


    You should also use the 'collections syntax' when accessing form
    elements to make your code compatible with more browsers:

    document.forms['register'].elements['FirstName'].value = "";
    document.forms['register'].elements['FirstName'].focus();

    However, as you seemed to pass the object that represents the
    FirstName field, this can be shortened to:

    FirstName.value = "";
    FirstName.focus();

    > }
    > }


    By the way, the validation above could be done with regular
    expressions, but I'd leave that for now. When you feel more
    comfortable with the language, look into them.

    It's a lot of changes, I know, but it's never too early to get into
    good habits.

    Mike

    --
    Michael Winter
    d (replace ".invalid" with ".uk")
     
    Michael Winter, Dec 13, 2003
    #2
    1. Advertising

  3. Sue wrote on 13 Dec 2003 at Sat, 13 Dec 2003 22:25:33 GMT:

    [Apologies for the second post, but it's clearer than responding to
    my first one.]

    > function validate(FirstName) {
    > var Characters =
    > "abcdefghijklmnopqrstuvwxyzABCDEFGHIGJLMNOPQRSTUVWXYZ"
    > var ok = "yes";
    > var temp;
    > for (var i=0; i<FirstName.value.length; i++) {
    > temp = "" + FirstName.value.substring(i, i+1);
    > if (Characters.indexOf(temp) == "-1") ok = "no";
    > }
    > if (ok == "no") {
    > alert("Please Enter a valid FirstName!");
    > document.Register.FirstName.value="";
    > document.Register.FirstName.focus();
    > }
    > }


    In addition to the changes I suggested in my other post, you could
    make another improvement, and one important change.

    The important change: in the nested if block, you should add a break
    statement. Without it, the alert boxes will be displayed for every
    invalid character, rather than just once, as the loop will continue
    until the entire string is checked. There is no need; if the value is
    invalid, it's invalid no matter how many illegal characters there
    are. Using the boolean I suggested:

    if (!ok) {
    window.alert(...);
    // skipped field value and focus changes

    // Exit from for loop
    break;
    }

    The suggestion: why use a variable to indicate a condition that
    should only occur once, is checked immediately after it altered, and
    only used that once? Basically, what I'm suggesting is to remove the
    'ok' variable altogether:

    for (...) {
    var temp = FirstName.value.charAt(i); // or .substr(i,1)
    // or .substring(i,i+1)
    if (-1 == Characters.indexOf(temp)) {
    window.alert(...);
    // skipped field value and focus changes

    // Exit from for loop
    break;
    }
    }

    The same thing can be done with the 'temp' variable, which makes the
    entire function:

    function validate(FirstName) {
    var Characters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIGJLMNOPQRSTUVWXYZ";

    for (var i = 0; i < FirstName.value.length; ++i) {
    if (-1 == Characters.indexOf(FirstName.value.charAt(i))) {
    window.alert("Please enter a valid first name.");

    FirstName.value = "";
    FirstName.focus();

    // Exit from for loop
    break;
    }
    }
    }

    And one final tip: don't use an if statement to set a boolean. That
    is, instead of (using the above):

    if (-1 == Characters.indexOf(temp)) ok = false;

    ...do:

    ok = (-1 != Characters.indexOf(temp));

    The comparison will evaluate to a boolean (false if String.indexOf()
    returns -1, true otherwise) and assign this to the variable. The
    first way appears to be more readable to start with, but the second
    is considered better (it should be more efficient for one thing,
    which is desirable in a loop). If it troubles you, well, that's what
    comments are for. :)

    Mike

    --
    Michael Winter
    d (replace ".invalid" with ".uk")
     
    Michael Winter, Dec 14, 2003
    #3
  4. Sue

    Sue Guest


    >The comparison will evaluate to a boolean (false if String.indexOf()
    >returns -1, true otherwise) and assign this to the variable. The
    >first way appears to be more readable to start with, but the second
    >is considered better (it should be more efficient for one thing,
    >which is desirable in a loop). If it troubles you, well, that's what
    >comments are for. :)
    >
    >Mike


    <SNIP>


    Michael,

    I appreciate your help. I have waited over a year for this JavaScript
    class and it still did not make so I had to take it online and have
    not had the benefit of lectures. With just the book, it was a "monkey
    see, monkey do" course.

    After spending several hours trying to make and understand the changes
    you suggested I became totally lost in trying to follow your second
    page of instruction. If you have the time I would greatly appreciate
    your help and explanations on getting this thing to work.

    As I mentioned in my posting setting up and calling functions is
    giving me real problems. So an explanation on how and when to call a
    second function if I take you suggestion of using the function
    validate(FirstName) when I also have to validate the lastname.

    Thanks,

    Sue




    <!-- Hide from old browsers

    function validate() {
    var Characters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIGJLMNOPQRSTUVWXYZ";
    var ok = true;
    var temp1;
    for (var i = 0; i < FirstName.value.length; ++i) {
    // The text only mention ++i and i++ and warned
    against using it at this point that is why I am trying to become more
    familiar with it now.

    if (-1 == Characters.indexOf(FirstName.value.charAt(i))) ok=false;
    {
    // charAt() was only mention in the Appendix of my
    textbook

    if (!ok) {
    // (! was not in the text but I did find it in the
    Appendix

    window.alert("Please Enter a valid FirstName!");
    // window was mention in the intro of the book but
    there was no real explanation of it.


    document.forms['Register'].elements['FirstName'].value="";

    document.forms['Register'].elements['FirstName'].focus();
    // for now
    document.forms['Register'].elements['FirstName'].focus() is a little
    easier for me to understand

    break;
    }
    }

    else {

    // begin validation check for lastname

    if (ok) {
    var ok = true;
    // I am guessing that this will reset "ok"
    before starting to check lastname.

    var temp2;
    for (var i = 0; i < LastName.value.length;
    ++i) {
    if (-1 ==
    Characters.indexOf(LastName.value.charAt(i))) ok=false; {
    if (!ok)
    window.alert("Please Enter a valid
    LastName!");

    document.forms['Register'].elements['LastName'].value="";

    document.forms['Register'].elements['LastName'].focus();
    break;
    }
    }
    }
    // End -->
     
    Sue, Dec 14, 2003
    #4
  5. Sue wrote on 14 Dec 2003 at Sun, 14 Dec 2003 20:34:47 GMT:

    > After spending several hours trying to make and understand the
    > changes you suggested I became totally lost in trying to follow
    > your second page of instruction. If you have the time I would
    > greatly appreciate your help and explanations on getting this
    > thing to work.


    My apologies. I thought that the second post might have been
    overkill, the inclusion of the break statement (to prevent your,
    'name is invalid' message from being repeated) was important to note.
    This time, I'll try to make a better effort to explain what I
    present. It's easy to take for granted some knowledge, particularly
    when you don't know the other person. I'm sorry if I now go the other
    way and explain too much (and sound patronising), but it'll be safer.

    What I'll do is present a simple version, then progressively refine
    it, explaining the differences. If I go too far, you can simply
    ignore it for now and use what you do understand. That way, you'll
    only be using code that you could write yourself. As I'm sure you
    know, blind copying and pasting never does anyone any good.

    > As I mentioned in my posting setting up and calling functions is
    > giving me real problems. So an explanation on how and when to
    > call a second function if I take you suggestion of using the
    > function validate(FirstName) when I also have to validate the
    > lastname.


    From what you showed, it seems that the validation for both the first
    and surname is the same. Because of this, there will be one function
    to do the common validation, and another that will call it for the
    two fields. This will allow for different messages for the different
    fields.

    I didn't know how you're performing the validation when I made the
    first two posts (I didn't follow your other threads). However, as
    you're validating on submission (something I should have assumed - my
    fault), one central function is better.

    <FORM ... name="example" onsubmit="return validate(this)">
    <INPUT ... name="first name">
    <INPUT ... name="surname">

    <!-- Just so you know (for in a moment),
    this sentence is in an SGML comment -->

    <!-- Place in HEAD. -->
    <SCRIPT type="text/javascript">
    // Notice that there are no SGML comments here. The likelihood of
    // encountering a browser that doesn't support in-line scripts is
    // *incredibly* low, and using such comments can sometimes do more
    // harm than good, so you can safely remove them. You can also
    // avoid problems by placing the script in a .js file, and using
    // the src attribute in a SCRIPT element to include it.

    // Validates the form. If it returns false, the form will not be
    // submitted. If it returns true, the form will submit as normal.
    function validate( form ) {
    // 'form' contains a reference to the form above and will provide
    // us with a way to access the controls. It is equivalent to:
    // var form = document.forms['example'];

    // The function, isNameValid, is described later, but it's name
    // (and purpose) is fairly self-explanatory.
    if ( false == isNameValid( form.elements['first name'].value )) {

    // window is an object that represents the browser window that
    // contains the page (represented by document). It is always
    // accessible, and contains information such as the status bar
    // text, and what frames that window contains. alert is simply
    // one of its methods.
    window.alert( 'Please enter a valid first name.' );

    // If you replace "form" with "document.forms['example']",
    // these are exactly the same as the lines in my previous
    // posts.
    form.elements['first name'].value = '';
    form.elements['first name'].focus();

    // Returning false will cancel the submission of the form.
    return false;
    }

    if ( false == isNameValid( form.elements['surname'].value )) {
    window.alert( 'Please enter a valid surname.' );
    form.elements['surname'].value = '';
    form.elements['surname'].focus();
    return false;
    }

    // Continue other validation steps. Return false (like above) if
    // the validation fails. Do nothing if it succeeds (so that the
    // return statement below is executed).

    return true;
    }

    // Validates a name (string). If the name is valid, this function
    // returns true. If not, it returns false.
    function isNameValid( name ) {
    // Here, unlike your original function, name is a string, not an
    // object. As this function only checks the value of a control
    // and doesn't modify that control, only the value is needed.

    var n = name.length; // Length of the string
    var valid = true;
    var validCharacters =
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    // In this statement below, there is little difference between
    // i++ and ++i. Using the pre-increment operator (++i) can
    // sometimes be quicker (probably isn't here), so I use it out of
    // habit. Does your text explain the difference between the two
    // and, more importantly, does it do it well?
    for (var i = 0; i < n; ++i) {

    // Using .substr( i, 1 ) or .substring( i, i + 1 ) is
    // equivalent to .charAt( i ), but as the first two are
    // designed to return more than one character, it is more
    // appropriate to use charAt (and probably quicker, too).
    var temp = name.charAt( i );

    // As originally, if the character being checked isn't part of
    // the allowed set, assign false to 'valid'.
    if ( -1 == validCharacters.indexOf( temp )) valid = false;

    // ! is a logical operator. It inverts a boolean value (true
    // becomes false, and visa versa). It also works on non-
    // booleans, by converting them to a boolean first, then
    // inverting[1]. You can read the below as: if not valid
    // then...
    if ( !valid ) return false;
    }
    // The statement above immediately exits the function with the
    // value false as soon as an invalid character is found. If we
    // get this far, the string is valid.
    return true;
    }
    </SCRIPT>

    In your attempt (which I've snipped as this is a long post), you were
    nearly correct here:

    if (ok) {
    var ok = true;
    // I am guessing that this will reset "ok" before starting to
    // check lastname.

    All you needed to do was:

    if (ok) {

    1) The variable, ok, was already defined, so there was no need to do
    it again with var ("ok = true" would have been fine).
    2) For that statement to be executed, ok would already have to be
    true, so there's no need to give it a value it already has.

    While what I've shown above should work exactly as you want it to, it
    isn't what you'd find on the Web, and it's not what I'd write. So,
    from here on out, I'll be refining the code until I end with the
    'regular expression' version I mentioned before. If there's anything
    below that I didn't explain thoroughly enough and you'd like to
    understand, do ask.

    The validate function is the easiest to start with; there's only one
    change that I'd make. I've reproduced the whole function, though.

    function validate( form ) {
    // Just as "if ( boolean == true )" can be reduced to
    // "if ( boolean )", we can change
    // "if ( false == isNameValid(...))" to
    // "if ( true != isNameValid(...))", which can be reduced to
    if ( !isNameValid( form.elements['first name'].value )) {
    // That would be read as, "if not isNameValid"

    window.alert( 'Please enter a valid first name.' );
    form.elements['first name'].value = '';
    form.elements['first name'].focus();
    return false;
    }

    if ( !isNameValid( form.elements['surname'].value )) {
    window.alert( 'Please enter a valid surname.' );
    form.elements['surname'].value = '';
    form.elements['surname'].focus();
    return false;
    }

    // Other validation steps go here, returning false if they fail.

    return true;
    }

    Initially, the isNameValid function is just as easy to modify. If you
    look at the 'valid' and 'temp' variables, you can see that they both
    are used and modified once each (ignoring the initialisation of
    'valid'). This means that they're presence is not needed. A variable
    is only really needed when its values will be read or modified
    several times during its life-time[2]. To correct this, it identifier
    will be replaced by the expression that was assigned it (that should
    make sense in a moment).

    First, temp is removed:

    function isNameValid( name ) {
    var n = name.length;
    var valid = true;
    var validCharacters =
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    for (var i = 0; i < n; ++i) {
    if ( -1 == validCharacters.indexOf( name.charAt( i )))
    valid = false;

    if ( !valid ) return false;
    }
    return true;
    }

    Notice that "name.charAt( i )" (the value assigned to 'temp') has now
    replaced 'temp'. Removing valid is a bigger leap, so it will be done
    in two stages. If you remember in my second post, I said that one
    should never use an if statement to assign to a boolean (like the
    first of the two if statements above does). To see why, consider
    this:

    if ( boolean ) {
    something = true;
    } else {
    something = false;
    }

    If 'boolean' is true, 'something' will be assigned true. If it is
    false, false will be assigned. It should be obvious that the same
    thing can be done this way:

    something = boolean;

    With 'valid', we have this situation:

    if ( boolean ) {
    something = false;
    } else {
    something = true;
    }

    'valid' is assigned the opposite of the if expression: if the index
    *is* -1, valid is false. So if the index *is not* -1, valid is true:

    function isNameValid( name ) {
    var n = name.length;
    var valid = true;
    var validCharacters =
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    for (var i = 0; i < n; ++i) {
    // If the index is not -1, valid is true:
    valid = ( -1 != validCharacters.indexOf( name.charAt( i ));

    if ( !valid ) return false;
    }
    return true;
    }

    We can now apply to 'valid' what we did to 'temp':

    function isNameValid( name ) {
    var n = name.length;
    var validCharacters =
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    for (var i = 0; i < n; ++i) {
    if ( !( -1 != validCharacters.indexOf( name.charAt( i )))
    return false;

    // You should notice that there are two NOT (!) operators
    // above (well, 'not' and 'not equal'). These cancel each other
    // out, so the above can (and should) be written as:
    // if ( -1 == validCharacters.... )
    }
    return true;
    }

    That is a much more compact function, but it can still be improved by
    one more thing: use of regular expressions. In fact, the operation is
    trivial with their use. I'll present the solution first, then explain
    it. You might want to find a good reference for regular expressions.
    Netscape's JavaScript Guide[3] does a fairly good job of explaining
    them. Someone else might have a better guide though (if they get this
    far into the post! :)

    function isNameValid( name ) {
    return /^[a-z]+$/i.test( name );
    }

    That's it! Pretty neat, huh? However, time to explain it...

    First of all, that is a literal regular expression. The two forward
    slashes (/) denote it. You can also create them with the RegExp
    object, but literals should be compiled by default (rather than call
    the RegExp.compile() method), so they execute faster. The
    ..test( name ) is a normal method call (test returns true if the
    string, name, matches the expression, false otherwise), so the part
    before that, "/^[a-z]+$/i" is the actual expression.

    After the second slash, you can specify flags. JavaScript has three:
    g, which makes the match global (repeated throughout the string); i,
    which makes it case-insensitive (so 'a' and 'A' are the same); and m,
    which caters for multi-line strings (that's new, though). I used i as
    you allow both character cases.

    Brackets ([]) specify a character set. This can be a range or
    individual characters: [abcd] is the same as [a-d]. So far, we are
    looking for a match with any character (only one, though) between A
    and Z, irrespective of case.

    We want more that one character so this is where the plus (+) comes
    in; it means that one or more of the preceding character (or set, in
    this case) must match.

    Finally, the carat (^) and dollar ($): these mean that the match goes
    from the beginning of the string (^) to the end of the string ($),
    respectively.

    So overall, /^[a-z]+$/i means: look for a match where the whole
    string contains one or more alphabetic characters, in any letter
    case. If you want to allow an empty string, replace the plus with an
    asterix (*); this allows zero or more characters in the set.

    As you can see, regular expressions can be extremely powerful; I
    replaced six statements in the reduced function with one. However,
    they can be *very* complex, but you can break them down like I did.
    The best advice though, would be to explain in plain English what
    they do - it makes far more sense.

    Hope that helps,

    Mike


    [1] I can explain that in more detail separately, if you want.

    [2] There is at least one exception to that rule, and the variable,
    n, is an example. In many languages, accessing an attribute
    (property) of an object takes more time and processing than that
    of a local variable. To increase efficiency, particularly with
    values that won't change (like the length of the string in
    isNameValid), these values can be stored and referenced locally,
    removing the need to query the object for the value. There may
    not be much difference with JavaScript (it isn't like a compiled
    executable), and it would really depend upon the implementation
    of the interpreter whether this kind of local storage would be
    beneficial. It is one of my habits, but shouldn't have any
    negative impact.

    [3] I used an older version of the guide (v1.3). This is a link to
    the Regular Expression chapter in the newest guide (v1.5). It's
    wrapped, of course.

    http://devedge.netscape.com/library/manuals/2000/javascript/1.5/guide
    /regexp.html#1010922

    Look at the examples and experiment with them. Create your own
    expressions: you'll get the hang of it. When it comes to checking
    for patterns in strings, nothing is better than a regular
    expression.

    --
    Michael Winter
    d (replace ".invalid" with ".uk")
     
    Michael Winter, Dec 15, 2003
    #5
  6. Sue

    Sue Guest

    <SNIP>

    Mike said,
    >
    > Look at the examples and experiment with them. Create your own
    > expressions: you'll get the hang of it. When it comes to checking
    > for patterns in strings, nothing is better than a regular
    > expression.


    Mike,

    You have given me a lot to study. Hopefully, this will help me improve
    my project so that I will be able to follow everything from top to
    bottom and have the project as a future referance and learning aid.

    I will try to figure all of this out and if you do not mind, I will
    call on you for help when I run into problems.

    Thanks
    Sue
     
    Sue, Dec 15, 2003
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Joshua
    Replies:
    8
    Views:
    6,055
    Joshua
    Sep 29, 2004
  2. judith
    Replies:
    5
    Views:
    337
    schouery
    Oct 16, 2006
  3. Replies:
    3
    Views:
    590
    Bernd Strieder
    Aug 11, 2006
  4. Sue
    Replies:
    7
    Views:
    157
    Richard Cornford
    Dec 10, 2003
  5. Sue
    Replies:
    8
    Views:
    139
    Thomas 'PointedEars' Lahn
    Dec 13, 2003
Loading...

Share This Page