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

S

Sue

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();
}
}
 
M

Michael Winter

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

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.

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
 
M

Michael Winter

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
 
S

Sue

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

Michael Winter

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.
 
S

Sue

<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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top