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

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Sue

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



    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(FirstN ame) {
    var Characters =
    "abcdefghijklmn opqrstuvwxyzABC DEFGHIGJLMNOPQR STUVWXYZ"
    var ok = "yes";
    var temp;
    for (var i=0; i<FirstName.val ue.length; i++) {
    temp = "" + FirstName.value .substring(i, i+1);
    if (Characters.ind exOf(temp) == "-1") ok = "no";
    }
    if (ok == "no") {
    alert("Please Enter a valid FirstName!");
    document.Regist er.FirstName.va lue="";
    document.Regist er.FirstName.fo cus();
    }
    }

  • Michael Winter

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

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

    <snip>
    [color=blue]
    > 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.[/color]

    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>
    [color=blue]
    > function validate(FirstN ame) {
    > var Characters =
    > "abcdefghijklmn opqrstuvwxyzABC DEFGHIGJLMNOPQR STUVWXYZ"[/color]

    You missed the semi-colon (;).
    [color=blue]
    > var ok = "yes";[/color]

    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
    }
    [color=blue]
    > var temp;
    > for (var i=0; i<FirstName.val ue.length; i++) {
    > temp = "" + FirstName.value .substring(i, i+1);[/color]

    Unless you omitted the contents for this example, remove the quotes.
    They do nothing here (String.substri ng() returns a String object).
    [color=blue]
    > if (Characters.ind exOf(temp) == "-1") ok = "no";[/color]

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

    if (-1 == Characters.inde xOf(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 )
    [color=blue]
    > }
    > if (ok == "no") {[/color]

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

    if (!ok) {
    // If not ok, alert the user
    }
    [color=blue]
    > alert("Please Enter a valid FirstName!");[/color]

    You should qualify that method call properly:

    window.alert(.. .);
    [color=blue]
    > document.Regist er.FirstName.va lue="";
    > document.Regist er.FirstName.fo cus();[/color]

    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 ();
    [color=blue]
    > }
    > }[/color]

    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
    M.Winter@blueyo nder.co.invalid (replace ".invalid" with ".uk")

    Comment

    • Michael Winter

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

      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.]
      [color=blue]
      > function validate(FirstN ame) {
      > var Characters =
      > "abcdefghijklmn opqrstuvwxyzABC DEFGHIGJLMNOPQR STUVWXYZ"
      > var ok = "yes";
      > var temp;
      > for (var i=0; i<FirstName.val ue.length; i++) {
      > temp = "" + FirstName.value .substring(i, i+1);
      > if (Characters.ind exOf(temp) == "-1") ok = "no";
      > }
      > if (ok == "no") {
      > alert("Please Enter a valid FirstName!");
      > document.Regist er.FirstName.va lue="";
      > document.Regist er.FirstName.fo cus();
      > }
      > }[/color]

      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.inde xOf(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(FirstN ame) {
      var Characters =
      "abcdefghijklmn opqrstuvwxyzABC DEFGHIGJLMNOPQR STUVWXYZ";

      for (var i = 0; i < FirstName.value .length; ++i) {
      if (-1 == Characters.inde xOf(FirstName.v alue.charAt(i)) ) {
      window.alert("P lease 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.inde xOf(temp)) ok = false;

      ...do:

      ok = (-1 != Characters.inde xOf(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
      M.Winter@blueyo nder.co.invalid (replace ".invalid" with ".uk")

      Comment

      • Sue

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

        [color=blue]
        >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[/color]

        <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(FirstN ame) when I also have to validate the lastname.

        Thanks,

        Sue




        <!-- Hide from old browsers

        function validate() {
        var Characters =
        "abcdefghijklmn opqrstuvwxyzABC DEFGHIGJLMNOPQR STUVWXYZ";
        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.inde xOf(FirstName.v alue.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("P lease 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.inde xOf(LastName.va lue.charAt(i))) ok=false; {
        if (!ok)
        window.alert("P lease Enter a valid
        LastName!");

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

        document.forms['Register'].elements['LastName'].focus();
        break;
        }
        }
        }
        // End -->

        Comment

        • Michael Winter

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

          Sue wrote on 14 Dec 2003 at Sun, 14 Dec 2003 20:34:47 GMT:
          [color=blue]
          > 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.[/color]

          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.
          [color=blue]
          > 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(FirstN ame) when I also have to validate the
          > lastname.[/color]

          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="retur n 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.f orms['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 =
          'abcdefghijklmn opqrstuvwxyzABC DEFGHIJKLMNOPQR STUVWXYZ';

          // 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 =
          'abcdefghijklmn opqrstuvwxyzABC DEFGHIJKLMNOPQR STUVWXYZ';

          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.charA t( 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 =
          'abcdefghijklmn opqrstuvwxyzABC DEFGHIJKLMNOPQR STUVWXYZ';

          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 =
          'abcdefghijklmn opqrstuvwxyzABC DEFGHIJKLMNOPQR STUVWXYZ';

          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.


          /regexp.html#101 0922

          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
          M.Winter@blueyo nder.co.invalid (replace ".invalid" with ".uk")

          Comment

          • Sue

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

            <SNIP>

            Mike said,[color=blue]
            >
            > 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.[/color]

            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

            Comment

            Working...