35

I've got an input field in the body of my document, and I need to format it as the user types. It should have parenthesis around the area code and a dash between the three and four digits after that.

Ex: (123) 456 - 7890

As the user types it should look something like:

(12
(123)
(123) 456
(123) 456 - 78
(123) 456 - 7890

Legendary_Linux
  • 1,509
  • 1
  • 11
  • 17
  • If you need to enforce this convention, rather than playing around with what they're typing, consider giving the user 3 input boxes, `() - ` with a couple _placeholders_ to give them hints about what to write (i.e. your `123 456 7890`) – Paul S. May 05 '15 at 17:00
  • To add on to what @PaulS. said, even better would be to allow them to enter in the value however they like and, as long as they have entered in 10 digits, autoformat their input to the format that you want, on `blur` (or `change` . . . your preference). Ex. Input: **1234567890** or **123-456-7890** or **123 456!?@#7890** . . . Output (in all cases): **(123) 456 - 7890** – talemyn May 05 '15 at 18:02

15 Answers15

85

New ES6 Answer

You can still do this using some simple JavaScript.

HTML

<input id="phoneNumber" maxlength="16" />

JavaScript (ES6)

const isNumericInput = (event) => {
    const key = event.keyCode;
    return ((key >= 48 && key <= 57) || // Allow number line
        (key >= 96 && key <= 105) // Allow number pad
    );
};

const isModifierKey = (event) => {
    const key = event.keyCode;
    return (event.shiftKey === true || key === 35 || key === 36) || // Allow Shift, Home, End
        (key === 8 || key === 9 || key === 13 || key === 46) || // Allow Backspace, Tab, Enter, Delete
        (key > 36 && key < 41) || // Allow left, up, right, down
        (
            // Allow Ctrl/Command + A,C,V,X,Z
            (event.ctrlKey === true || event.metaKey === true) &&
            (key === 65 || key === 67 || key === 86 || key === 88 || key === 90)
        )
};

const enforceFormat = (event) => {
    // Input must be of a valid number format or a modifier key, and not longer than ten digits
    if(!isNumericInput(event) && !isModifierKey(event)){
        event.preventDefault();
    }
};

const formatToPhone = (event) => {
    if(isModifierKey(event)) {return;}

    const input = event.target.value.replace(/\D/g,'').substring(0,10); // First ten digits of input only
    const areaCode = input.substring(0,3);
    const middle = input.substring(3,6);
    const last = input.substring(6,10);

    if(input.length > 6){event.target.value = `(${areaCode}) ${middle} - ${last}`;}
    else if(input.length > 3){event.target.value = `(${areaCode}) ${middle}`;}
    else if(input.length > 0){event.target.value = `(${areaCode}`;}
};

const inputElement = document.getElementById('phoneNumber');
inputElement.addEventListener('keydown',enforceFormat);
inputElement.addEventListener('keyup',formatToPhone);

And if you'd like to fiddle with it:
https://jsfiddle.net/rafj3md0/

Disclaimer:
It's worth noting this gets a little weird if you attempt to modify the middle of the number because of the way browsers handle caret placement after you set an element's value. Solving that problem is doable, but would require more time than I have right now, and there are libraries out there that handle things like that.


Old ES5 Answer

You can do this using a quick javascript function.

If your HTML looks like:
<input type="text" id="phoneNumber"/>

Your JavaScript function can simply be:

// A function to format text to look like a phone number
function phoneFormat(input){
        // Strip all characters from the input except digits
        input = input.replace(/\D/g,'');
        
        // Trim the remaining input to ten characters, to preserve phone number format
        input = input.substring(0,10);

        // Based upon the length of the string, we add formatting as necessary
        var size = input.length;
        if(size == 0){
                input = input;
        }else if(size < 4){
                input = '('+input;
        }else if(size < 7){
                input = '('+input.substring(0,3)+') '+input.substring(3,6);
        }else{
                input = '('+input.substring(0,3)+') '+input.substring(3,6)+' - '+input.substring(6,10);
        }
        return input; 
}

Of course, you'll need an event listener:

document.getElementById('phoneNumber').addEventListener('keyup',function(evt){
        var phoneNumber = document.getElementById('phoneNumber');
        var charCode = (evt.which) ? evt.which : evt.keyCode;
        phoneNumber.value = phoneFormat(phoneNumber.value);
});

And unless you're okay storing phone numbers as formatted strings (I don't recommend this), you'll want to purge the non-numeric characters before submitting the value with something like:
document.getElementById('phoneNumber').value.replace(/\D/g,'');

If you'd like to see this in action with bonus input filtering, check out this fiddle:
http://jsfiddle.net/rm9vg16m/

// Format the phone number as the user types it
document.getElementById('phoneNumber').addEventListener('keyup', function(evt) {
  var phoneNumber = document.getElementById('phoneNumber');
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  phoneNumber.value = phoneFormat(phoneNumber.value);
});

// We need to manually format the phone number on page load
document.getElementById('phoneNumber').value = phoneFormat(document.getElementById('phoneNumber').value);

// A function to determine if the pressed key is an integer
function numberPressed(evt) {
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  if (charCode > 31 && (charCode < 48 || charCode > 57) && (charCode < 36 || charCode > 40)) {
    return false;
  }
  return true;
}

// A function to format text to look like a phone number
function phoneFormat(input) {
  // Strip all characters from the input except digits
  input = input.replace(/\D/g, '');

  // Trim the remaining input to ten characters, to preserve phone number format
  input = input.substring(0, 10);

  // Based upon the length of the string, we add formatting as necessary
  var size = input.length;
  if (size == 0) {
    input = input;
  } else if (size < 4) {
    input = '(' + input;
  } else if (size < 7) {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6);
  } else {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6) + ' - ' + input.substring(6, 10);
  }
  return input;
}
Enter a phone number here: <input type="text" id="phoneNumber" onkeypress="return numberPressed(event);" />
Legendary_Linux
  • 1,509
  • 1
  • 11
  • 17
  • 1
    You should store phone numbers as _String_, yes it will take more memory but it will be futureproof when you start needing numbers beginning with e.g. `0` or `+` – Paul S. May 05 '15 at 16:58
  • @PaulS I think he meant _formatted string_... and I'd agree with that. No need to keep the formatting in the database (or wherever). It's still a string, it just won't have the `[( -]` characters. – canon May 05 '15 at 17:03
  • 2
    I did indeed mean a formatted string. I have edited the answer to reflect this. – Legendary_Linux May 05 '15 at 18:46
  • 1
    @Legendary_Linux I laughed when you wrote // I am lazy and don't like to type things more than once and didn't even use the variable right below it – Chasen Bettinger Sep 06 '18 at 15:21
  • 1
    Just splitting hairs, but the first 3 numbers in a US phone isn't the "zip [code]", it's the "area code." – bobbyz Oct 14 '20 at 21:57
  • Whoops! You are correct, @bobbyz. I have updated the answer to reflect the change. – Legendary_Linux Oct 23 '20 at 20:40
  • This is pretty great. I see that it allows an open paren for the area code, but not a close paren. Is there a way to fix that? – Jonathan Tuzman Mar 30 '22 at 12:14
  • nice! god blass you! – devnull Ψ May 12 '23 at 12:39
10

Earlier answers didn't consider what happens when a user makes a mistake and deletes some of the entered digits.

For those looking for a jQuery solution, this reformats on every keyup event, and removes the additional characters and whitespace when the user is editing the number.

$('#phone').keyup(function(e){
    var ph = this.value.replace(/\D/g,'').substring(0,10);
    // Backspace and Delete keys
    var deleteKey = (e.keyCode == 8 || e.keyCode == 46);
    var len = ph.length;
    if(len==0){
        ph=ph;
    }else if(len<3){
        ph='('+ph;
    }else if(len==3){
        ph = '('+ph + (deleteKey ? '' : ') ');
    }else if(len<6){
        ph='('+ph.substring(0,3)+') '+ph.substring(3,6);
    }else if(len==6){
        ph='('+ph.substring(0,3)+') '+ph.substring(3,6)+ (deleteKey ? '' : '-');
    }else{
        ph='('+ph.substring(0,3)+') '+ph.substring(3,6)+'-'+ph.substring(6,10);
    }
    this.value = ph;
});
Motate
  • 207
  • 3
  • 4
10

I studied the various solutions here and came up with this, which I believe is short, sweet, and simple. It still leaves the cursor at the end if you remove an interior digit, but otherwise works perfectly. This is the kind of solution I came here looking for, and what I think the OP wanted.

HTML

<input  id="phone" onInput="this.value = phoneFormat(this.value)"/>

Javascript

function phoneFormat(input) {//returns (###) ###-####
    input = input.replace(/\D/g,'').substring(0,10); //Strip everything but 1st 10 digits
    var size = input.length;
    if (size>0) {input="("+input}
    if (size>3) {input=input.slice(0,4)+") "+input.slice(4)}
    if (size>6) {input=input.slice(0,9)+"-" +input.slice(9)}
    return input;
}

Update I found a way to make it even a tiny bit neater:

Javascript

function phoneFormat(input) {//returns (###) ###-####
    input = input.replace(/\D/g,'');
    var size = input.length;
    if (size>0) {input="("+input}
    if (size>3) {input=input.slice(0,4)+") "+input.slice(4,11)}
    if (size>6) {input=input.slice(0,9)+"-" +input.slice(9)}
    return input;
}
Jay Reeve
  • 111
  • 1
  • 4
8

I'm not a fan of the slicing stuff. I'd advise using .replace(), pass it a regex, capture the pieces of the phone number, and then output it the way you need it. If you can read regex, it's a much better programmatic way to approach the issue, and dead simple to alter the format.

var phoneNumber = "1234567899";

var formatted = phoneNumber.replace(/(\d{1,2})(\d{1})?(\d{1,3})?(\d{1,4})?/, function(_, p1, p2, p3, p4){
  let output = ""
  if (p1) output = `(${p1}`;
  if (p2) output += `${p2})`;
  if (p3) output += ` ${p3}`
  if (p4) output += ` ${p4}`
  return output;
});

Note: I haven't added any sort of whitespace, non number stripping but you can add that as well.

Rey
  • 3,639
  • 5
  • 33
  • 40
4

Use this function for any type of formation mask

function numericStringMask(str, mask) {
  if (!mask) return str;

  const numeric = str.replaceAll(/[^\d]/g, '');

  let idx = 0;

  const formated = mask.split('').map(el => {
    if (el === '#') {
      el = numeric[idx];
      idx++;
    }
    return el;
  });

  return formated.join('');
}

Example 1: (123) 456 - 7890

numericStringMask('1234567890', '(###) ### - ####')

Example 2: (123) 456-7890

numericStringMask('1234567890', '(###) ###-####')

Example 3: (11) 90056-7890

numericStringMask('11900567890', '(##) #####-####')

briancollins081
  • 845
  • 9
  • 19
mfabiodias
  • 41
  • 2
3

This solution:
- Formats the phone number input like the iPhone phone app does (at least in the US)
- Allows the user to backspace
- Resets the cursor position to where it was before executing the function

It was made with the help of some of the answers above, hope it helps someone

let previousPhone = ''
function phoneFormat(field) {
    const specialCharCount = (field.value.match(/\D/g) || []).length;
    let cursorPosition = field.selectionStart;

    let input = field.value.replace(/\D/g,'');
    const size = input.length;
    if (input.substring(0,1) == 1) {
        if (size===0) {input=``}
        else if (size<2) {input=`+${input} `}
        else if (size<4) {input=`+${input.substring(0,1)} (${input.substring(1)}`}
        else if (size<8) {input=`+${input.substring(0,1)} (${input.substring(1,4)}) ${input.substring(4)}`}
        else if (size<12) {input=`+${input.substring(0,1)} (${input.substring(1,4)}) ${input.substring(4,7)}-${input.substring(7,11)}`}
    }else{
        if (size>7 && size<11) {input=`(${input.substring(0,3)}) ${input.substring(3,6)}-${input.substring(6)}`}
        else if (size>3 && size<8) {input=`${input.substring(0,3)}-${input.substring(3)}`}
    }
    
    if (input !== previousPhone) {
        previousPhone = input
        const specialCharDiff = (input.match(/\D/g) || []).length - specialCharCount;
        cursorPosition += specialCharDiff

        field.value = input
        field.selectionStart = cursorPosition;
        field.selectionEnd = cursorPosition;
    }
}
<input type="tel" oninput="phoneFormat(this)">
DesVallees
  • 31
  • 2
2

let telEl = document.querySelector('#phoneNum')

telEl.addEventListener('keyup', (e) => {
  let val = e.target.value;
  e.target.value = val
    .replace(/\D/g, '')
    .replace(/(\d{1,4})(\d{1,3})?(\d{1,3})?/g, function(txt, f, s, t) {
      if (t) {
        return `(${f}) ${s}-${t}`
      } else if (s) {
        return `(${f}) ${s}`
      } else if (f) {
        return `(${f})`
      }
    });
})
Phone Number: <input type="text" id="phoneNum" maxlength="14" />
venkat7668
  • 2,657
  • 1
  • 22
  • 26
1

I recently stumbled across the same problem, here is the solution I used. It is quite simple and also checks the form before the user submits the form.

function myFunction() {
  var inpField = document.getElementById("myInput");
  var l = inpField.value.length;
  var key = event.inputType;
  var toDelete = (key == 'deleteContentBackward' || key == 'deleteContentForward') ? 'delete' : 'keep';
  //deleteContentBackward and deleteContentForward are obtained when user hits backspace or delete keys. To get extra info from inputType, check: InputEvent https://www.w3schools.com/jsref/obj_inputevent.asp
  if (toDelete === 'delete') {
    alert('Please, enter your number again.');
    // clears the whole input field
    inpField.value = "";
  }
  // then, checks the inputs to add the required pattern. Also helps the user to check if number is typed correctly
  switch (l) {
    case 1:
      inpField.value = "(" + inpField.value;
      break
    case 4:
      inpField.value = inpField.value + ") ";
      break
    case 9:
      inpField.value = inpField.value + " - ";
      break
    case 17:
      // prevents user from typing more numbers than necessary:
      inpField.value = inpField.value.slice(0, l - 1)
  }
}
<form>
  <input type="tel" id="myInput" oninput="myFunction()" placeholder="(###) ### - ####" required pattern="\([0-9]{3}\) [0-9]{3} - [0-9]{4}">
  <!-- oninput is trigued when the input changes, check https://www.w3schools.com/jsref/event_oninput.asp -->
  <!-- Used regex to check the input value before submit from user -->
  <input type="submit">
</form>
(123) 456 - 7890
1

While it's true that @legendary_Linus answer works link to that here https://stackoverflow.com/a/30058928/16559479

This is so much simpler

HTML

<input id="phoneNumber" placeholder="Phone Number" maxlength="16"/>

JAVASCRIPT

    const formatToPhone = (event) => {
    
        event.target.value=event.target.value.replace(/\D/g,'');//this enforces that input is numeric
    
        const input = event.target.value.substring(0,10); // First ten digits of input only
        const areaCode = input.substring(0,3);
        const middle = input.substring(3,6);
        const last = input.substring(6,10);
    
        if(input.length > 6){event.target.value = `(${areaCode}) ${middle} - ${last}`;}
        else if(input.`enter code here`length > 3){event.target.value = `(${areaCode}) ${middle}`;}
        else if(input.length > 0){event.target.value = `(${areaCode}`;}
    };
        const inputElement = document.getElementById('phoneNumber');
        inputElement.addEventListener('change',formatToPhone);

here is the code in action https://jsfiddle.net/1xiscool/5146s7pv/2/

0

To add some additional ease for the user, I'd actually update the string to automatically include a ")" or "-" as the user reaches certain characters, to prevent them from adding, say two dashes. (555)555--5555

if(size === 0) {
    input = input;
} 
else if (size === 3) {
    input = '('+input.substring(0,3)+') '
}
else if (size < 4) {
    input = '('+input;
}
else if (size === 6) {
    input = '('+input.substring(0,3)+') '+input.substring(3,6)+' -';
}
else if (size > 6) {
    input = '('+input.substring(0,3)+') '+input.substring(3,6)+' - '+input.substring(6,10);
}
return input
paulby
  • 87
  • 2
  • 8
0

I don't have enough points to add a comment to @Rey's answer above, but you don't need to do all that in order to take the backspace or delete key into account. All you have to do is if the delete or backspace key was pressed, return the string as it is already because the formatting will already be there.

The OP asked for a pure javascript solution, so I have included a function to capture the event keyCode. You could do that in jQuery if you are following @Rey's answer.

document.getElementById('phoneNumber').addEventListener('keyup', function(evt) {
    var charCode = (evt.which) ? evt.which : evt.keyCode;
    if(charCode == 8 || charCode == 46) return true;
    else this.value = formatPhone(this.value);
}, false);

function formatPhone(str) {
    if(str.length == 0) return str;
    if(delKey) return str;
    var phone = '(';
    str = str.replace(/\D/g, '').substring(0,10);
    if(str.length < 3) {
        phone += str;
    } else if(str.length < 6) {
        phone += str.substring(0, 3) + ') ';
        if(str.length > 3) phone += str.substring(3, str.length);
    } else {
        phone += str.substring(0, 3) + ') ' + str.substring(3, 6) + '-' + str.substring(6, 10);
    }
                
    return phone;
}
wuijin
  • 19
  • 2
  • This answer fails to address instances where a user presses backspace or delete when their carat placement is mid-string. In those cases, if the number of characters present would require a re-formatting of the display, the display would not be updated. – Legendary_Linux Mar 12 '21 at 18:49
0

Here is a simple solution in React.

(getFormattedPhone and getDigitOnlyPhone are from the answer that @Legendary_Linux gave)

We pass a formatted value to our input so that it correctly displays like this: (123) 456 - 7890

In the onChange, we use getDigitOnlyPhone to pass up a string of digits, rather than our formatted phone number.

import React from 'react'

const getDigitOnlyPhone = value =>
  value.toString().replace(/\D/g, '').substring(0, 10)

const getFormattedPhone = value => {
  if (!value) return ''

  const phone = getDigitOnlyPhone(value)
  const areaCode = phone.substring(0, 3)
  const middle = phone.substring(3, 6)
  const last = phone.substring(6, 10)

  let formattedPhone = ''
  if (phone.length > 6) {
    formattedPhone = `(${areaCode}) ${middle} - ${last}`
  } else if (phone.length > 3) {
    formattedPhone = `(${areaCode}) ${middle}`
  } else if (phone.length > 0) {
    formattedPhone = `(${areaCode}`
  }

  return formattedPhone
}

export const PhoneInput = ({ value, onChange, ...props }) => (
  <input
    {...props}
    maxLength={16}
    pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
    value={getFormattedPhone(value)}
    onChange={e => {
      const phone = getDigitOnlyPhone(e.target.value)
      if (value || phone) onChange(phone)
    }}
  />
)
Appel21
  • 226
  • 1
  • 7
0

let telEl = document.querySelector('#phoneNum')

telEl.addEventListener('keyup', (e) => {
  let val = e.target.value;
  e.target.value = val
    .replace(/\D/g, '')
    .replace(/(\d{1,4})(\d{1,3})?(\d{1,3})?/g, function(txt, f, s, t) {
      if (t) {
        return `(${f}) ${s}-${t}`
      } else if (s) {
        return `(${f}) ${s}`
      } else if (f) {
        return `(${f})`
      }
    });
})
Phone Number: <input type="text" id="phoneNum" maxlength="14" />
HyderYash
  • 46
  • 7
0

For those of you looking for something similar but in the format of ###-###-#### or #-###-###-#### (just dashes, no parentheses) I implemented the below. Pass in the full input field as a parameter.

This function also has the following additional benefits:

  • dynamically handles 10 digit OR 11 digit numbers with leading country codes (e.g. 11 digit numbers in the format <COUNTRY_CODE>-###-###-####).

  • automatically resets the cursor position back to where the user expects it to be (e.g. if you made a typo and needed to delete/insert a new digit in the middle of the number, the function will input the number, update the format, and then put the cursor back to where the new digit was inserted (instead of moving the cursor to the end of the input like most solutions do).

const formatPhoneNum = (inputField) => {
    const nums = inputField.value.split('-').join("");
    const countryCode = '1';
    const digits = nums[0] === countryCode ? 1 : 0;

    // get character position of the cursor:
    let cursorPosition = inputField.selectionStart;

    // add dashes (format 1-xxx-xxx-xxxx or xxx-xxx-xxxx):
    if (nums.length > digits+10) {
        inputField.value = `${digits === 1 ? nums.slice(0, digits) + '-' : ""}` + nums.slice(digits,digits+3) + '-' + nums.slice(digits+3,digits+6) + '-' + nums.slice(digits+6,digits+10);
    }
    else if (nums.length > digits+6) {
        inputField.value = `${digits === 1 ? nums.slice(0, digits) + '-' : ""}` + nums.slice(digits,digits+3) + '-' + nums.slice(digits+3,digits+6) + '-' + nums.slice(digits+6,nums.length);
    }
    else if (nums.length > digits+5) {
        inputField.value = `${digits === 1 ? nums.slice(0, digits) + '-' : ""}` + nums.slice(digits,digits+3) + '-' + nums.slice(digits+3,nums.length);
    }
    else if (nums.length > digits+3) {
        inputField.value = `${digits === 1 ? nums.slice(0, digits) + '-' : ""}` + nums.slice(digits, digits+3) + '-' + nums.slice(digits+3, nums.length);
    }
    else if (nums.length > 1 && digits === 1) {
        inputField.value = nums.slice(0,digits) + '-' + nums.slice(digits, nums.length);
    }

    // reseting the input value automatically puts the cursor at the end, which is annoying,
    // so reset the cursor back to where it was before, taking into account any dashes that we added...
    // if the character 1 space behind the cursor is a dash, then move the cursor up one character:
    if (inputField.value.slice(cursorPosition-1, cursorPosition) === '-') {
        cursorPosition++;
    }
    
    inputField.selectionStart = cursorPosition;
    inputField.selectionEnd = cursorPosition;
}
<input type="tel" pattern='^(1-)?[0-9]{3}-[0-9]{3}-[0-9]{4}' oninput='formatPhoneNum(this)' placeholder="Phone Number">
J Davis
  • 11
  • 3
0

This works great for 10-digit US numbers and covers issues like backspacing, removing letters, and changing numbers after typing.

const formatPhone = (phoneNumberString, remove = false) => {
    let newPhoneNumberString = ("" + phoneNumberString).replace(/[a-zA-Z]/g, "")
    let cleaned = ("" + newPhoneNumberString).replace(/\D/g, "").slice(0, 10)
    if (remove) {
        return cleaned
    }
    if (formatPhone(newPhoneNumberString, true).length == 10) {
        let match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/)
        if (match) {
            return "(" + match[1] + ") " + match[2] + "-" + match[3]
        }
    }
    return newPhoneNumberString
}

To show formatted (555) 555-5555:

console.log(phoneFormat(phoneValue))

To show raw 555555555 (numbers only; good for storage):

console.log(phoneFormat(phoneValue, true))
Chad French
  • 61
  • 2
  • 4