Legendary_Linux
Legendary_Linux

Reputation: 1559

Format a phone number as a user types using pure JavaScript

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

Upvotes: 35

Views: 98470

Answers (15)

Legendary_Linux
Legendary_Linux

Reputation: 1559

New ES 2024 Answer

It's even simpler now.

HTML

<input type="tel" id="phone" placeholder="(123) 456 - 7890" maxlength="16" />

JS

window.addEventListener('load', () => {
  const phoneInput = document.querySelector('#phone');
  phoneInput.addEventListener('keydown', disallowNonNumericInput);
  phoneInput.addEventListener('keyup', formatToPhone);
});

const disallowNonNumericInput = (evt) => {
  if (evt.ctrlKey) { return; }
  if (evt.key.length > 1) { return; }
  if (/[0-9.]/.test(evt.key)) { return; }
  evt.preventDefault();
}

const formatToPhone = (evt) => {
  const digits = evt.target.value.replace(/\D/g,'').substring(0,10);
  const areaCode = digits.substring(0,3);
  const prefix = digits.substring(3,6);
  const suffix = digits.substring(6,10);

  if(digits.length > 6) {evt.target.value = `(${areaCode}) ${prefix} - ${suffix}`;}
  else if(digits.length > 3) {evt.target.value = `(${areaCode}) ${prefix}`;}
  else if(digits.length > 0) {evt.target.value = `(${areaCode}`;}
};

Very 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);" />

Upvotes: 89

Chad French
Chad French

Reputation: 61

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

Upvotes: 0

Desvall&#233;es
Desvall&#233;es

Reputation: 31

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

Upvotes: 3

mfabiodias
mfabiodias

Reputation: 41

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', '(##) #####-####')

Upvotes: 4

J Davis
J Davis

Reputation: 11

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

Upvotes: 0

Trent Jones
Trent Jones

Reputation: 11

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/

Upvotes: 1

Jay Reeve
Jay Reeve

Reputation: 151

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

Upvotes: 14

HyderYash
HyderYash

Reputation: 56

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" />

Upvotes: 0

Daniel Vidal
Daniel Vidal

Reputation: 19

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

Upvotes: 1

Appel21
Appel21

Reputation: 246

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)
    }}
  />
)

Upvotes: 0

wuijin
wuijin

Reputation: 47

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

Upvotes: 0

venkat7668
venkat7668

Reputation: 2767

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" />

Upvotes: 2

Motate
Motate

Reputation: 237

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

Upvotes: 10

paulby
paulby

Reputation: 87

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

Upvotes: 0

Rey
Rey

Reputation: 3767

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.

Upvotes: 8

Related Questions