homepage Welcome to WebmasterWorld Guest from 54.167.185.110
register, free tools, login, search, pro membership, help, library, announcements, recent posts, open posts,
Become a Pro Member

Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
Forum Library, Charter, Moderator: open

JavaScript and AJAX Forum

    
Regex phone number woes
Regex, Phone number validation
Randomgnome




msg:3912229
 12:32 am on May 13, 2009 (gmt 0)

Hello all. I've been trying in vain to get a regex that will validate multiple formats for US telephone numbers. I'm new to writing regex and I'm just at the end of my rope.

I'm wanting it to match the following formats

1234567890
123 456 7890
123-456-7890
(123) 456-7890
(123) 456 7890

Matches such as (123 456-7890 are acceptable as well

I've been able to get the first 3 of the above to match, its the presence of the ( )s that seems to be throwing me. Here is what I stopped with (or something similar). If anyone could supply me with a regex that would work and break down how it works I'd appreciate it. Thanks!

^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$

 

astupidname




msg:3912249
 1:09 am on May 13, 2009 (gmt 0)

It seems to be working just fine for me without any alterations.. perhaps you are testing it incorrectly? (treating the bracketted items as numbers?) Keep in mind that all values coming from forms (the most likely place for you to need to validate a phone number) will be strings, and test this, it comes out all true for me:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>Some Title</title>

</head>
<body>
<div>
<script type="text/javascript">

//given that any phone number you need to validate
//will most likely come from a form input,
//all phone numbers will be strings
var phArr = [
'1234567890',
'123 456 7890',
'123-456-7890',
'(123) 456-7890',
'(123) 456 7890'
];

var s = '';

var oRex = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;

for (var i = 0; i < phArr.length; i++) {
s += oRex.test(phArr[i]) + "<br>";
}

document.write(s);

</script>
</div>
</body>
</html>


Perhaps you could present a case where it is not working correctly?

Randomgnome




msg:3912268
 1:47 am on May 13, 2009 (gmt 0)

Let me give you the sections of code that deal with the regex:

The following is 2 calls from a custom "class" defined in the same client.js file. d is defined as "document.theForm" and the values array is the various values from the form.


d.getElementById("phone").value = v.validate(v.isPhone(values[4]),"Please Enter a valid Phone Number","document.getElementById('phone').style.border = 'solid 1px #FF0000'; document.getElementById('phone').style.color = '#FF0000'");

Here are the 2 functions being called from the "v" object. The check for regex is is the "isPhone" function. I'm pretty certain I've got it right. Please note that returning the value as an array instead of a string equates to false and trips the validate function (I've included it if that doesn't make sense).


this.validate = function(val,err,doThis) {
if (val.constructor.toString().indexOf("Array") == -1) {
return val;
} else {
if (!error) {
if ((val.length > 1) && (val[1] != "")) { alert(val[1]); } else { alert(err); }
error = true;
}
if (doThis != "undefined") {
if (val.length > 2) { eval(val[2]); } else { eval(doThis); }
}
return val[0];
}
}


this.isPhone = function(phone) {
var filter = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;
if (!filter.test(phone)) {
return [phone]; //fail
}
return phone;
}

Thanks for the help :-)

[edited by: Randomgnome at 1:53 am (utc) on May 13, 2009]

astupidname




msg:3912418
 7:58 am on May 13, 2009 (gmt 0)

The check for regex is is the "isPhone" function. I'm pretty certain I've got it right. Please note that returning the value as an array ? instead of a string equates to false and trips the validate function (I've included it if that doesn't make sense).

this.isPhone = function(phone) {
var filter = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;
if (!filter.test(phone)) {
return [phone]; //fail, of course, this is an incorrect declaration of an array
}
return phone;
}

Yeah, that doesn't make any sense to me, as to why you are trying to return that way. Usually there is really no need to return the actual phone number, and the isPhone function could just be like:
this.isPhone = function(phone) {
var filter = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;
return filter.test(phone); //return true or false
}

Also, a comment or two I had on your validate function:

this.validate = function(val,err,doThis) {
if (val.constructor.toString().indexOf("Array") == -1) {
//the above condition equates exactly to:
// if (!(val instanceof Array)){ //if val is not an Array
return val;
}

And apparently you like to use eval and have not heard to stay away from it. There is always a better way, you will find anonymous functions very useful in this regards.
The code could be entirely reworked to be more like:

d.getElementById("phone").value = v.validate(
v.isPhone(values[4]), //true or false value being returned here
"Please Enter a valid Phone Number",
function () { //rather than evaluating a string (eval is evil), pass in an anonymous function
var el = document.getElementById('phone');
el.style.color = '#FF0000';
//Safari doesn't like single border declarations such as 'solid 1px #FF0000',
//at least not in javascript and I've had issues with it in past,
//so break it down to the individual properties
el.style.borderWidth = '1px';
el.style.borderStyle = 'solid';
el.style.borderColor = '#FF0000';
}
);

this.validate = function(val,err,doThis) {
if (!(val instanceof Array)){ //if val is not an Array
return val;
} else {
if (!error) {
if ((val.length > 1) && (val!= "")) {
alert(val[1]);
} else {
alert(err);
}
error = true;
}
if (doThis != "undefined") {
if (val.length > 2 && typeof val[2] == 'function') {
val[2](); //place an anonymous function in val[2] from now on, don't use eval
} else if (typeof doThis == 'function') {
doThis();
}
}
return val[0];
}
}

this.isPhone = function(phone) {
var filter = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;
return filter.test(phone);
}

Anyhow, the above is untested and just meant to show possible improvements to those parts of the overall code. And actually, I don't understand what the first else block's first if statement's first if statement is for in the validate function: (if ((val.length > 1) && (val!= "")) {
alert(val[1]);
}. ), but, no matter to me. If you still have trouble/questions, come on back, and by the way.. welcome to webmasterworld!

Randomgnome




msg:3912779
 6:09 pm on May 13, 2009 (gmt 0)

Thanks for the help. This project is actually in its early phases which is why the one line declarations and use of eval is present. I generally will use them in order to keep the code a bit easier to read for myself and any of the other techs that may take a look at it. Once the logic is good and I know the regex is working properly I remove the potentially buggy bits.

I was actually going to add another function to the "class" which accepts a reference to the form field, d.SomeField, as an argument. That function would change out the borders and font color. Assuming the received value equates to false it calls that function and passes the reference, which was in turn passed to it. The phone value is returned because most of the validation functions (isPhone, for example) also cleans the string of white space and various undesirable characters (sql injection concerns mostly). As our form submits into a database and a CRM I need to ensure the strings are as clean as possiable. I've never had an issue declaring an array as someVar = [val1,val2,val3,...]; but if it gives me issues I can switch it over to return new Array(phone);

The odd && condition is from a previous iteration of this project, which I just missed . Thanks for pointing that out for me.

I should have a chancse to make these changes and test them out in a few hours. If I have any further difficulities with the regex I'll let you know. I did have a few questions however:

The test that I'm doing in the isPhone function is correct? Will it return an array of matches or just a Boolean true?
Can you explain to me when I would use the pares ( and ) in a regex? I'm learning regex off online tutorials and forum discussions and there just isn't a whole lot of good info out there. From what I can tell using ^[0-9a-zA-Z]+$ would require any alphanumeric character to appear at least once and accept it up to an infinite number of characters, matching basically any alphanumeric string. What would the difference between that and ^([0-9a-zA-Z])+$ be?

edit: hckd on fniks dinn wrk fer mee!

[edited by: Randomgnome at 6:10 pm (utc) on May 13, 2009]

astupidname




msg:3913122
 1:30 am on May 14, 2009 (gmt 0)

I did have a few questions however:

The test that I'm doing in the isPhone function is correct? Will it return an array of matches or just a Boolean true?

Depends on how you use it of course, using .test() always returns true or false. Using: phone.match(filter); would return either null or array of match/matches.

As for being correct, it appears so, but there are a few flaws. There is not any checking that if one '(' is present a ')' should be present also and in proper place, ditto the '-' characters. It does get a bit complicated when supporting 5 or more different formats, which is a fine thing to do, but may desire more thorough checking even yet.
var filter = /^\(?([0-9]{3})+\)?[\-\s]?([0-9]{3})+[\-\s]?([0-9]{4})+$/;
The above won't precisely match whether there is one '(' there should be a ')' and etc.., also, instead of using [0-9] use \d

var filter = /^\d{10}$^\d{3}\s\d{3}\s\d{4}$^\d{3}-\d{3}-\d{4}$^\(\d{3}\)\s\d{3}(-\s)\d{4}$/;
The above is one long-form RegExp that will precisely match the following 5 formats:
'1234567890'
'123 456 7890'
'123-456-7890'
'(123) 456-7890'
'(123) 456 7890'
(Just break the mRex apart at each 'or' conditional for each format, with the last check checking the two final formats using (-\s), there may be a shorter way yet, but is sufficient. It all depends of course on your own requirements. Also note this forum wrecks the or conditionals and they may need to be replaced before usage.)

Can you explain to me when I would use the pares ( and ) in a regex?

Dependant on context. In filter (the second one) above I had to use them in the final check that contains (-\s) to denote that either of those characters: '-' or single space: \s, were allowed in that particular slot. The parens are for generally grouping characters to create a clause, also used for capturing particular character groups when .match()'ing. Such as you want to pull the fourth word out of a string:

<script type="text/javascript">

var str = 'Astringoftext';

var strM = str.match(/^[a-zA-Z0-9]+$/);
var strM2 = str.match(/^([a-zA-Z0-9]+)$/);

alert(strM + "\n" + strM2);
//note that strM2 returns two items,
//the first one being the entire match it's self as the first item in
//the array returned. Second item in the array in this case is
//the captured part via the parens (). A better example:

var str = 'A string of text';

var strM = str.match(/^([a-z]+)\s([a-z]+)\s([a-z]+)\s([a-z]+)$/i);
//because of the parens, each set of characters
//contained in the parens above will be captured by the match method.
//strM basically verifies there are exactly 4 words
//seperated by a space and captures- () -each grouping of characters
if (strM !== null) {
//note there will actually be 5 items returned in the array of matches.
//strM will return the entire match as the first item in array of matches
//and each individual match as the rest of the array's items
alert(strM + "\n\n" + strM[0] + "\n\n" + strM[4]);
}

/*****
or even better, we only want to capture the fourth word, so only use the parens on it:
*****/
var str = 'A string of text';

var strM = str.match(/^[a-z]+\s[a-z]+\s[a-z]+\s([a-z]+)$/i); //just capturing ([a-z]+) the fourth character grouping
//because of the parens, each set of characters
//contained in the parens above will be captured by the match method.
//strM basically verifies there are exactly 4 words
//seperated by a space and captures- () -THE LAST WORD
if (strM !== null) {
//note there will actually be 2 items returned in the array of matches.
//strM will return the entire match as the first item in array of matches
//and each individual captured () match as the rest of the array's items
alert(strM + "\n\n" + strM[0] + "\n\n" + strM[1]);
}
</script>


astupidname




msg:3913127
 1:44 am on May 14, 2009 (gmt 0)

d.getElementById("phone").value = v.validate(

And I must confess, I know my earlier post doesn't support the above very well actually, as you are trying to assign new value to the input. I don't see why you would want to do that anyways... , should just notify the user to fix their entry. My earlier example is actually more intended to be something more like:
var someVar = v.validate( //yadda yadda yadda... etc...

if (someVar) {// or: return someVar; or: do some other stuff....

jalarie




msg:3962718
 8:38 pm on Jul 30, 2009 (gmt 0)

I wrote a function a while back that recognizes over 40 different valid US phone number formats and returns it in whatever format you want. The visitor may enter "123-456-7890" and you could get it back as "(123)456-7890" if you wish. Sticky me if you'd like a copy.

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved