Search code examples
javascriptregexknockout.jsknockout-binding-handlers

Format phone number as you type (xxx) xxx - xxxx


I am writing a custom bindingHandler in knockout to format the (US) phone numbers in the following format as they type it.

(xxx) xxx - xxxx

HTML code...

 <input type="text" data-bind="phoneNumber: phone" />

And binding handler...

 ko.bindingHandlers.phoneNumber = {
    init: function(element, valueAccessor, allBindings) {
        ko.bindingHandlers.value.init(element,valueAccessor, allBindings);
        },    
    update: function(element, valueAccessor) {
        var phone = ko.utils.unwrapObservable(valueAccessor());
        if (!phone) return;
        var output;
        input = phone;
        input = input.replace(/[^0-9]/g, '');
        var areaCode = input.substr(0, 3);
        var nextThreeDigits = input.substr(3, 3);
        var lastFourDigits = input.substr(6, 4); 

        if (areaCode.length < 3) {
            output = "(" + areaCode;
        } else if (areaCode.length == 3 && nextThreeDigits.length < 3) {
          output = "(" + areaCode + ")" + " " + nextThreeDigits;
        } else if (areaCode.length == 3 && nextThreeDigits.length == 3) {
          output = "(" + areaCode + ")" + " " + nextThreeDigits + "-" + lastFourDigits;
        }

        var phoneObs = valueAccessor();
        phoneObs(output);
    }
};

The following code works as expected. The one flow with this approach is deleting a phone number using backspace. Once it deletes the last four digits and hits the - it can't delete the - nor ( ) since I'm dynamically adding the - and ( ) based on the length. I can use the arrow keys or click before those symbols and start deleting, or I can highlight the textbox and delete all at once. But I need to be able to remove the symbols by backspace or simulate and remove it from the code dynamically.

Any suggestions on how I can resolve this issue?

UPDATE

Here's JSFIDDLE. The only issue I'm having with this fiddle is it doesn't update the values on value change. It only updates if you press enter or leave textbox. Not sure If I'm missing anything else...

I have updated my logic to account for the issue I am experiencing, which is fixed. But I have noticed that both logic have one more issue.

If I have a phone number

(123) 456-7890

If I remove 1 from the area code, the cursor doesn't stay at the same place (before 2). It moves to the end of string and everything shifts one number down.

if (areaCode.length <= 3 && nextThreeDigits == "") {
    output = "(" + areaCode;
} else if (areaCode.length == 3 && nextThreeDigits < 3) {
    output = "(" + areaCode + ")" + " " + nextThreeDigits;
} else if (areaCode.length == 3 && nextThreeDigits.length <= 3 && lastFourDigits.length == "") {
    output = "(" + areaCode + ")" + " " + nextThreeDigits;
} else if (areaCode.length == 3 && nextThreeDigits.length == 3 && lastFourDigits.length <= 4) {
  output = "(" + areaCode + ")" + " " + nextThreeDigits + "-" + lastFourDigits;
}

Solution

  • how about using an input mask (jasny has a nice one http://www.jasny.net/bootstrap/ ) and a custom binding.

    here is a fiddle where I did it.http://jsfiddle.net/vL59q8e1/2/

    here is the javascript for the binding

    ko.bindingHandlers.inputmask = {
      init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        ko.bindingHandlers.value.init(element, valueAccessor, allBindings);
    
         $(element).inputmask({
            mask: '(999) 999-9999'
            });
      },
      update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
          }
    };