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.


<!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>


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);

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?

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.

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.


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

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


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.


var classRegExp = /(^| )(\\S+)\\b/g;
classRegExp.lastIndex = 0;

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.

IE has the same behaviour in that regard.

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.

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.

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/Core_JavaScript_1.5_Reference/Objects/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:


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

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


var classRegExp = /(^| )(\\S+)\\b/g;
classRegExp.lastIndex = 0;

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.

You can fix the problem by creating a new instantiation of the regular expression, instead of assigning the same regular expression.

Replace:


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

With:


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

and you should see better results.

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? :slight_smile:
    Just tested it again in Google Chrome and I think M.Firefox behaves in the same way?!

Alex.

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?

Thank you so much!