SitePoint Sponsor

User Tag List

Results 1 to 13 of 13

Thread: Form Validation

  1. #1
    SitePoint Zealot
    Join Date
    Nov 2009
    Posts
    115
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Form Validation

    This code is taken from Simply JavaScript book. It looks like it has a bug which is not described in Errata section of a book. This validation works perfectly well in IE but not in FireFox and I don't know where there problem is.

    Description of a problem: if you click Submit button, without filling out User name (see html mark up, please), an alert message will show up saying: "Please fill in this required field". If an end user is pretty dumb and decides to hit Submit button again, for the second time, without entering any data in User name field, he will be taken to Email address field. So, basically User name is omitted from validation.

    I think a problem might have to do with exec method, executed on the class string on the second Submit event. It behaves as if User name did not have any class, and therefore this field's validation is omitted.

    Can anybody help? Problem is encountered in Mozilla Firefox and Chrome. IE6 works just fine.

    Code HTML4Strict:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
      <head>
        <title>Reusable Form Validation</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <link rel="stylesheet" type="text/css" href="formvalidation.css" />
        <script type="text/javascript" src="core.js"></script>
        <script type="text/javascript" src="formvalidation.js"></script>
      </head>
      <body>
        <h1>A Form with Client-Side Validation</h1>
        <form action="thanks.html" method="post">
          <div>
            <label for="user">User name:
              <input type="text" class="red required" id="username" name="username" />
            </label>
          </div>
          <div>
            <label for="email">Email address:
              <input type="text" class="email" id="email" name="email" />
            </label>
          </div>
          <div>
            <input type="submit" value="Submit" />
          </div>
        </form>
      </body>
    </html>
    Code JavaScript:
    var FormValidation =
    {
      init: function()
      {
        var forms = document.getElementsByTagName("form");
     
        for (var i = 0; i < forms.length; i++)
        {
          Core.addEventListener(forms[i], "submit", FormValidation.submitListener);
        }
      },
     
      rules:
      {
        required: /./,
        requiredNotWhitespace: /\S/,
        positiveInteger: /^\d*[1-9]\d*$/,
        positiveOrZeroInteger: /^\d+$/,
        integer: /^-?\d+$/,
        decimal: /^-?\d+(\.\d+)?$/,
        email: /^[\w\.\-]+@([\w\-]+\.)+[a-zA-Z]+$/,
        telephone: /^(\+\d+)?( |\-)?(\(?\d+\)?)?( |\-)?(\d+( |\-)?)*\d+$/
      },
     
      errors:
      {
        required: "Please fill in this required field.",
        requiredNotWhitespace: "Please fill in this required field.",
        positiveInteger: "This field may only contain a positive whole number.",
        positiveOrZeroInteger: "This field may only contain a non-negative whole number.",
        integer: "This field may only contain a whole number.",
        decimal: "This field may only contain a number.",
        email: "Please enter a valid email address into this field.",
        telephone: "Please enter a valid telephone number into this field."
      },
     
      submitListener: function(event)
      {
        var fields = this.elements;
     
        for (var i = 0; i < fields.length; i++)
        {
          var className = fields[i].className;
          var classRegExp = /(^| )(\S+)\b/g;
          var classResult;
          while (classResult = classRegExp.exec(className))
          {
            var oneClass = classResult[2];
            var rule = FormValidation.rules[oneClass];
            if (typeof rule != "undefined")
            {
              if (!rule.test(fields[i].value))
              {
                fields[i].focus();
                alert(FormValidation.errors[oneClass]);
                Core.preventDefault(event);
                return;
              }
            }
          }
        }
      }
    };
     
    Core.start(FormValidation);

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Can you please let us know what page(s) of the book, or parts of the code-archive for the book, the above example comes from?
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Zealot
    Join Date
    Nov 2009
    Posts
    115
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by pmw57 View Post
    Can you please let us know what page(s) of the book, or parts of the code-archive for the book, the above example comes from?
    Sure thing. Book is called: Simply JavaScript
    Chapter 6: Form Enhancements
    Section: Reusable Validation Form

    Code-archive: Forms: formvalidation.html and formvalidation.js

    I hope that helps and I would really like to get some feedback. Just to make sure I explain the problem right:

    1. Click Submit button without filling out any field.
    -- Alert box appears
    2. Don't fill out this form in question. Click Submit button again.
    -- Focus moves to the next field omitting first field
    ?!?! Why? :-)
    Just tested it again in Google Chrome and I think M.Firefox behaves in the same way?!

    Alex.

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Yes, it appears to be something interesting happening with the regex.

    One call it gets an array, and second identical call get null.
    It's flip/flopping back and forth between array and null, quit possibly due to the global search parameter.

    Will investigate further and see what I can find.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    You can fix the problem by creating a new instantiation of the regular expression, instead of assigning the same regular expression.

    Replace:
    Code javascript:
    var classRegExp = /(^| )(\S+)\b/g;

    With:
    Code javascript:
    var classRegExp = new RegExp('(^| )(\\S+)\\b', 'g');

    and you should see better results.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Instead of using the explicit RegExp, you can also keep the regexp as it initially was, but add a statement to reset the lastIndex parameter of the object.

    That way it will begin its global search from the start, instead of from its first match, and prevent the problem from occurring.

    See https://developer.mozilla.org/En/Cor...ts/RegExp/Exec for details on how the global setting affects the exec method

    Anyway, there are two solutions for this.

    One is the initially found RegExp:
    Code javascript:
    var classRegExp = new RegExp('(^| )(\\S+)\\b', 'g');

    and the other solution (my preferred one) is to reset the lastIndex parameter

    Code javascript:
    var classRegExp = /(^| )(\S+)\b/g;
    classRegExp.lastIndex = 0;
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  7. #7
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,233
    Mentioned
    47 Post(s)
    Tagged
    1 Thread(s)
    Oh, nice. Good info. Why does IE go back the the beginning then instead of starting from last match then? Or is that somewhat undefined and Mozilla chose a different route?

  8. #8
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Stomme poes View Post
    Oh, nice. Good info. Why does IE go back the the beginning then instead of starting from last match then? Or is that somewhat undefined and Mozilla chose a different route?
    IE has the same behaviour in that regard.
    http://msdn.microsoft.com/en-us/libr...33(VS.85).aspx

    Where I think the issue is occurring, is when assigning the regular expression to the variable. IE seems to create a completely new object, where as other web browsers to something different, resulting in the lastIndex property not being reset.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  9. #9
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,233
    Mentioned
    47 Post(s)
    Tagged
    1 Thread(s)
    So, this is specifically an exec() issue and not RegExp() in other cases issue?

    If the global flag is set for a regular expression, exec searches the string beginning at the position indicated by the value of lastIndex. If the global flag is not set, exec ignores the value of lastIndex and searches from the beginning of the string.
    Well, another bookmark, because I'll forget about this until I run into it as a bug myself.

  10. #10
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Stomme poes View Post
    So, this is specifically an exec() issue and not RegExp() in other cases issue?
    I don't think that exec is the issue, I think that it's one that involves constructing the same regular expression on to the same variable.

    From the example in the book, this fails, in that it causes the problem that was experienced.

    Code javascript:
    var classRegExp = /(^| )(\S+)\b/g;

    Using a more explicit form of the constructor fixes the problem.

    Code javascript:
    var classRegExp = RegExp('^| )(\\S+)\\b', 'g');

    So it's a good bet that RegExp is resetting lastIndex, whereas the former code is not.

    Explicitly setting the lastIndex property after the assignment also fixes the problem.

    Code javascript:
    var classRegExp = /(^| )(\S+)\b/g;
    classRegExp.lastIndex = 0;
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  11. #11
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Stomme poes View Post
    So, this is specifically an exec() issue and not RegExp() in other cases issue?
    I just understood what you're getting at here. The ol' noggin gets a bit foggy at 1am.

    Yes, the issue is related to using exec with the global flag, as that sets the lastIndex property to the location of the first match.

    Resetting that lastIndex property back to 0, whether by redeclaration, or more explicitly, is the concern of this issue.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  12. #12
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,233
    Mentioned
    47 Post(s)
    Tagged
    1 Thread(s)
    Why are all the kiwis I know routinely awake at ungodly hours?

    Anyway, thanks for the info. I haven't personally even gotten to the point of using exec() yet so it's one of those things where I'm trying to just wrap my mind around it for the immediate future. JS-phage.

  13. #13
    SitePoint Zealot
    Join Date
    Nov 2009
    Posts
    115
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by pmw57 View Post
    Instead of using the explicit RegExp, you can also keep the regexp as it initially was, but add a statement to reset the lastIndex parameter of the object.

    That way it will begin its global search from the start, instead of from its first match, and prevent the problem from occurring.

    See https://developer.mozilla.org/En/Cor...ts/RegExp/Exec for details on how the global setting affects the exec method

    Anyway, there are two solutions for this.

    One is the initially found RegExp:
    Code javascript:
    var classRegExp = new RegExp('(^| )(\\S+)\\b', 'g');

    and the other solution (my preferred one) is to reset the lastIndex parameter

    Code javascript:
    var classRegExp = /(^| )(\S+)\b/g;
    classRegExp.lastIndex = 0;
    Thank you so much!


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •