Format a phone number as a user types using pure J

2019-01-23 18:48发布

问题:

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

回答1:

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

    // I am lazy and don't like to type things more than once
    const target = event.target;
    const input = target.value.replace(/\D/g,'').substring(0,10); // First ten digits of input only
    const zip = input.substring(0,3);
    const middle = input.substring(3,6);
    const last = input.substring(6,10);

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

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



回答2:

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


回答3:

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.



回答4:

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