Link to home
Start Free TrialLog in
Avatar of tonyrodriguez
tonyrodriguez

asked on

I need a single javascript function to provide an input mask for a US Phone Number and Visa/MC Credit Card Number

Hi,

I need a javascript input mask for a US phone number and credit card. I want a single function that allows me to pass the format (mask) I want applied. The formats I need are below:
1. US Phone Number (999) 999-9999
2. Visa/MC CreditCard 9999-9999-9999-9999
3. both masks should only allow numeric digits

I have a function I am using for the phone number. It seems to work fine except when trying to backspace past the "-" and the first "(". It pushes back at this point. Other than that this function works ok. Maybe you can start with this function, fix it's backspace problem and then make it generic so I can just pass it a mask.

<script>
var style1="(###) ###-####";
var style2="#-(###) ###-####";
var nullKeys="-8-0-13-";

function valid(e){
  var k;
  if(document.all){k=window.event.keyCode;}
  else{
   
k=e.which;
    if(nullKeys.indexOf("-"+k+"-")!=-1){return;}
  }
  if(k<48||k>57){return false;}
  setTimeout("format();",100);
}
function format(){
var obj=document.NC_form.bPhone;
var txt=obj.value,n,style;
txt=txt.replace(/\D/g,"");
if(txt.length>10){style=style2;}else{style=style1;}
for(i=0;i<txt.length;i++){
  n=txt.charAt(i);
  if(!isNaN(n*1)&&n!=' '){n='#';}
  if(n!=style.charAt(i)){txt=txt.substring(0,i)+style.charAt(i)+txt.substring(i);}
}
if(style.charAt(i)!='#'){txt+=style.charAt(i);}
obj.value=txt;
}

document.NC_form.bPhone.onkeypress=valid;
</script>

thanks.

Avatar of Cem Türk
Cem Türk
Flag of Türkiye image

Hi tonyrodriguez,

> 8-0-13

Can explain what are this null keys?

Cheers
Nickson
tonyrodriguez,

Here's a sample mask code I've just completed. I didn't do any checks on those 8-0-13 checks as I didn't understand what they are.

<html>
<head>
<script>
function Clear(obj)    {
    if (obj.value.match(/^#+$/))    obj.value = "";
}

function Check(obj, len)    {
    if (obj.value!="")    {
        if (!obj.value.match(/^\d+$/))    {
            alert("Please enter digits only.");
            obj.value = obj.value.substr(0, obj.value.length-1);
        }
    }
    if (obj.value.length==len) {
        objbase = obj.name.replace(/^(.+)\d$/, "$1");
        nextobjno = obj.name.replace(/^.+(\d)$/, "$1")*1+1;
        if (document.getElementById(objbase + nextobjno))    {
            document.getElementById(objbase + nextobjno).focus();
        }
    }
}
</script>
</head>
<body>
<form name="form1" method="post" action="">
  <table width="100%"  border="0">
    <tr>
      <td width="13%">Phone Number</td>
      <td width="87%">(
        <input name="phone1" type="text" value="###" size="7" maxlength="3" onFocus="Clear(this)" onKeyUp="Check(this, 3);">
        )
        <input name="phone2" type="text" value="###" size="7" maxlength="3" onFocus="Clear(this)" onKeyUp="Check(this, 3);">
        -
        <input name="phone3" type="text" value="####" size="10" maxlength="4" onFocus="Clear(this)" onKeyUp="Check(this, 4);"></td>
    </tr>
    <tr>
      <td>Credit Card </td>
      <td><input name="cc1" type="text" value="#" size="3" maxlength="1" onFocus="Clear(this)" onKeyUp="Check(this, 1);">
        -(
        <input name="cc2" type="text" value="###" size="7" maxlength="3" onFocus="Clear(this)" onKeyUp="Check(this, 3);">
        )
        <input name="cc3" type="text" value="###" size="7" maxlength="3" onFocus="Clear(this)" onKeyUp="Check(this, 3);">
        -
        <input name="cc4" type="text" value="####" size="10" maxlength="4" onFocus="Clear(this)" onKeyUp="Check(this, 4);"></td>
    </tr>
  </table>
</form>
</body>
</html>


Nickson
Avatar of raj3060
BRB with solution
Avatar of tonyrodriguez
tonyrodriguez

ASKER

Hi,

I didn't want to break out the credit card and phone number into their composite pieces. I have one field for credit card that is is 19 chars long (16 digits and 3 hyphens). I want the mask to automatically apply the hyphens in the right spot (after every 4 chars) as the user types in the digits. I also have a single phone number field and I want the mask (###) ###-#### applied as the user types in the number. Maybe there is a misunderstanding of what I mean by mask. To me a mask is when a user types in the digits and the field is automatically formatted with the parenthesis or hyphens.

Tony
I will be back with your solution...
tonyrodriguez,

I will change the code to a single textbox. But just to let you know, for majority of the websites that I have been, credit card entry boxes are separated. This is easier to view and relate back to the card number which are also broken as such. The same theory would go for the phone number as a user would be easier to relate that the first set of numbers is for country code.

Nickson
tony,

I actually like your original code more than most of the suggested solutions... it works great for me, unmodified, in Internet Explorer.  Are you using Mozilla when you have problems?
OK, it took long but here is what you are looking for:

+++++++++++++++++++
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
<title>Untitled</title>
<script>
//Format Credit Card number
function formatCC(t) {
t = t.replace(/[^0-9]/g,"")
var n = t
if (t.length>4) n = t.substr(0,4)+"-"+t.substr(4,4)
if (t.length>8) n = t.substr(0,4)+"-"+t.substr(4,4)+"-"+t.substr(8,4)
if (t.length>12) n = t.substr(0,4)+"-"+t.substr(4,4)+"-"+t.substr(8,4)+"-"+t.substr(12,4)
return n
}
//End
//Validate Credit Cards
function validateCC(s) {
var v = "0123456789";
var w = "";
if(s.value.length>0){
for (var i=0; i < s.length; i++) {
x = s.charAt(i);
if (v.indexOf(x,0) != -1)
w += x;
}
var j = w.length / 2;
if (j < 6.5 || j > 8 || j == 7){
alert('Please enter a valid credit card number');
s.value=''
s.focus()
return false;
}}
var k = Math.floor(j);
var m = Math.ceil(j) - k;
var c = 0;
for (var i=0; i<k; i++) {
a = w.charAt(i*2+m) * 2;
c += a > 9 ? Math.floor(a/10 + a%10) : a;
}
for (var i=0; i<k+m; i++) c += w.charAt(i*2+1-m) * 1;
return (c%10 == 0);
}
//End
//Format telephone number
function formatTel(t) {
t = t.replace(/[^0-9]/g,"")
var n = t
//if (t.length<3) n = "("+t.substr(0,3)
if (t.length>3) n = "("+t.substr(0,3)+")"+t.substr(3,3)
if (t.length>6) n+= "-"+t.substr(6,4)
return n
}
//End
//Validate Phone number
function validTel(t) {
var x = t.value
if (x.length>0){    
     if(x.length<13){
     alert("Invalid telephone number.\r\nPlease enter 10 digit phone number starting with area code")
     t.value = "";
     t.focus()    
     }
}
}
//End
</script>
</head>
<body>
<p>Enter Phone#:<input type='text' name='txtphone' maxlength="13" onkeyup="this.value=formatTel(this.value);" onblur="validTel(this)" />
</p>
<p>Enter Credit Card#:<input type='text' name='ccnum' maxlength="19" onkeyup="this.value=formatCC(this.value);" onblur="validateCC(this)" />
</p>
</body>
</html>
+++++++++++++++++++
Credit card validation might not be working:

Use this instead:

+++++++++++++
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>
<title>Untitled</title>
<script>
//Format Credit Card number
function formatCC(t) {
t = t.replace(/[^0-9]/g,"")
var n = t
if (t.length>4) n = t.substr(0,4)+"-"+t.substr(4,4)
if (t.length>8) n = t.substr(0,4)+"-"+t.substr(4,4)+"-"+t.substr(8,4)
if (t.length>12) n = t.substr(0,4)+"-"+t.substr(4,4)+"-"+t.substr(8,4)+"-"+t.substr(12,4)
return n
}
//End
//Validate Credit Cards
function validateCreditCard(s) {
var v = "0123456789";
var w = "";
for (var i=0; i < s.length; i++) {
x = s.charAt(i);
if (v.indexOf(x,0) != -1)
w += x;
}
var j = w.length / 2;
if (j < 6.5 || j > 8 || j == 7) return false;
var k = Math.floor(j);
var m = Math.ceil(j) - k;
var c = 0;
for (var i=0; i<k; i++) {
a = w.charAt(i*2+m) * 2;
c += a > 9 ? Math.floor(a/10 + a%10) : a;
}
for (var i=0; i<k+m; i++)
c += w.charAt(i*2+1-m) * 1;
return (c%10 == 0);
}

function validateCC(n){
if(n.value.length>0){
if (!validateCreditCard(n.value)) {
  alert('Please enter a valid credit card number');
      n.focus()
  return false;
      }
}
}
//End

//Format telephone number
function formatTel(t) {
t = t.replace(/[^0-9]/g,"")
var n = t
//if (t.length<3) n = "("+t.substr(0,3)
if (t.length>3) n = "("+t.substr(0,3)+")"+t.substr(3,3)
if (t.length>6) n+= "-"+t.substr(6,4)
return n
}
//End
//Validate Phone number
function validTel(t) {
var x = t.value
if (x.length>0){    
     if(x.length<13){
     alert("Invalid telephone number.\r\nPlease enter 10 digit phone number starting with area code")
     t.value = "";
     t.focus()    
     }
}
}
//End
</script>
</head>
<body>
<p>Enter Phone#:<input type='text' name='txtphone' maxlength="13" onkeyup="this.value=formatTel(this.value);" onblur="validTel(this)" />
</p>
<p>Enter Credit Card#:<input type='text' name='ccnum' maxlength="19" onkeyup="this.value=formatCC(this.value);" onblur="validateCC(this)" />
</p>
</body>
</html>

+++++++++++++
dlwyatt82,

Yes I like the original code as well. That's why I was asking to follow that same pattern except make it generic so that I could call the same function and just pass it a different mask as a parameter and have it work. The only problem with the function was the backspacing across the hyphen and yes the problem was in Mozilla.

So right now I still don't have the type of solution I'm looking for.

Thanks.
I see, I missed that part of your question.  Unfortunately I don't have Mozilla installed at the office and can't test the portability of this code right now, but here's the general idea:

///////////////////////////////////////////////////////

<html>
<head>
<title>Testing</title>
<script>

var g_obj = null;
var g_style = null;

function valid(e, obj, style) {
  var k;
  if (window.event) {
    k = window.event.keyCode;
  } else if (e) {
    k = e.which;
    if (k == null || k == 0 || k == 8 || k == 13) return true;
  } else return false;
 
  if (k<48 || k>57) { return false; }
 
  g_obj = obj;
  g_style = style;
 
  setTimeout("format();",100);
 
  return true;
}

function format() {
  var obj = g_obj;
  var style = g_style;

  g_obj = null;
  g_style = null;

  if (obj == null || style == null) return;
 
  var txt=obj.value;
  var n;
 
  txt=txt.replace(/\D/g,"");
 
  for (i=0; i<txt.length; i++) {
    n = txt.charAt(i);
    if (!isNaN(n*1) && n!=' ') { n='#'; }
    if (n != style.charAt(i)) { txt = txt.substring(0,i) + style.charAt(i) + txt.substring(i); }
  }
 
  if(style.charAt(i) != '#') { txt += style.charAt(i); }
  obj.value=txt;  
}

</script>

</head>
<body>

<form name="NC_form">
<input type="text" name="bPhone" size="20" maxlength="14" onkeypress="return valid(event, this, '(###) ###-####');">
<br>
<input type="text" name="CCNum" size="25" maxlength="19" onkeypress="return valid(event, this, '####-####-####-####');">

</form>
</body>
</html>
ASKER CERTIFIED SOLUTION
Avatar of dlwyatt82
dlwyatt82
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
dlwyatt82,

This is more along the lines I was looking for. I don't mind the two events. I'll give it a try when I get home.

Thanks.
I copied this test page and saved it. The phone works perfect but the cc doesn't format the field at all. It allowed me to type in 19 digits. Can you take a look?
That's odd, it works fine for me.  But as I said, my only testing was in Internet Explorer.  Are you still having problems only in Mozilla?
@tonyrodriguez:

Did you try my solution?
Hi raj3060;

Yes I tried yours. It wasn't exactly what I was looking for but I tried it anyone. They both seem to work. It appears that you are actually validating the cc number for more than just numerics cuz it gave me an error until I put in a "real" credit card number. Is this true? Can you tell me what rules you are using? Do the rules take into account a mastercard as well or only a visa? I have another function that has all the credit card rules for instance mc needs to start with certain digits etc. So the plan was to just apply the mask while they type the number and then apply the rules on the final edit when they hit submit.  Also, is there a way you can allow the mask characters if the user enters them in the right position. For instance, while entering the credit card, if the user types a dash after the first four characters, that should be allowed. Right now you dissallow the character when I type it but then just put it back in right aftwards. This isn't a big deal so if it's too hard you can ignore it.

thanks.
It should consider MC and VISA both. Did you try both?
I can try to allow user dash.

@ tonyrodriguez:
LUHN formula, aka the Mod 10 algorithm is used to validate accurate credit card numbers. The whole purpose of autoformat is to automatically insert the mask.
Hi raj3060,

There's a weird problem. The mask works fine as long as I use the numbers key along the top of my keyboard. As soon as I use the number keys in the keypad the formatting doesn't work any more. Any ideas?
That is weird... for some reason (in IE, anyway - not sure how Mozilla behaves), the keyCodes passed to the onKeyPress and onKeyUp events are different in many cases (upper case vs lower case letters, and numpad keys vs normal keyboard numbers, for instance).  Here's an update to address that (you can uncomment the window.status lines if you want to see the different keyCodes for yourself):

<html>
<head>
<title>Testing</title>
<script>

function valid(e, obj, style) {
  var k;
  if (window.event) {
    k = window.event.keyCode;
  } else if (e) {
    k = e.which;
    if (k == null || k == 0 || k == 8 || k == 13) return true;
  } else return false;
 
  // window.status = k.toString();
  if (k < 48 || k > 57) { return false; }
 
  return true;
}

function format(e, obj, style) {
  var k = null;  

  var txt=obj.value;
  var n;
 
  if (window.event) {
    k = window.event.keyCode;
  } else if (e) {
    k = e.which;
  } else return;
 
  // window.status += ", " + k.toString();
 
  if (!((k >= 48 && k <= 57) || (k >= 96 && k <= 105))) return;
 
  txt = txt.replace(/\D/g,"");
 
  for (i=0; i<txt.length; i++) {
    n = txt.charAt(i);
    if (!isNaN(n*1) && n!=' ') { n='#'; }
    if (n != style.charAt(i)) { txt = txt.substring(0,i) + style.charAt(i) + txt.substring(i); }
  }
 
  if (style.charAt(i) != '#') { txt += style.charAt(i); }
  obj.value=txt;  
}

</script>

</head>
<body>

<form name="NC_form">
<input type="text" name="bPhone" size="20" maxlength="14" onkeypress="return valid(event);" onkeyup="format(event, this, '(###) ###-####');">
<br>
<input type="text" name="CCNum" size="25" maxlength="19" onkeypress="return valid(event);" onkeyup="format(event, this, '####-####-####-####');">

</form>
</body>
</html>
Just curious, which answer are you using?

If you are using my answer then you should have accepted my answer..
Anyway, I tested my solution and it works with bith number keys..
If you are interested I can work on mask, where it would allow user entered dashes too.
Let me know.
I was also a little confused, but he accepted my answer and then posted a technical question that was related to that solution (all the while typing your name).
I'm confused now. Originally I meant to accept the answer from Raj0306. I accidently accepted the answer from dlwyatt82. (dlwyatt82 mentioned a good point tho).Then after further testing I noticed that the answer from Raj0306 wasn't working for me when I entered the values from the keypad. Since the question was closed cuz I already accepted the answer, I opened a related question about the keypad problem. Everyone on the board convinced me that the problem was with my hardware. So for that question I selected the answer fro Raj0306 to make things even since I accidently selected dlwyatt82's answer on this question. I was just trying to even it out cuz this question was closed and raj0306 seemed upset that I accidently selected dlwyatt82' answer.

Anyways, it now appears that dlwyatt82 investigated and confirmed my problem. He noticed that the key codes passed are different from the keypad versus. So now he has really solved the problem so he should get credit. If that's the case, then I don't think you need to reopen cuz I already gave him credit for this question. It was just a different answer. I'm new to this whole thing so if it makes any difference then you can reopen it and I'll accept the answer from dlwyatt82 .

Sorry for the confusion.
I just replaced the function raj0306 gave me for the format with the function that dlwyatt82 provided and now everything works fine regardless of where which numeric keys were used.
@ tonyrodriguez : I am not upset. I was just wondering you asking question about the solution I provided. If @dlwyatt82 solution works for you, then you should give hm points.

Happy New Year.

Thanks