4
\$\begingroup\$

I created a form that formats phone numbers, allows only allowable keys to be pressed, and converts a lettered entry into its corresponding phone keypad number. Is there some cleanup or better coding that can be done? There is no business need for international phone numbers as it is a local business.

function pageLoad() {
    $(".PhoneSelection").keypress(function (e) {
        // Only allow number entry
        var regex = new RegExp("^[0-9]+$");
        var key = String.fromCharCode(!e.charCode ? e.which : e.charCode);
        if (!regex.test(key)) {
            // If not a number, is it a letter?
            var regex2 = new RegExp("^[A-Za-z]+$");
            if (regex2.test(key)) {
                // Convert the letter typed to the number on the phone keypad
                $(this).val($(this).val() + letterPos(key));
                return false;
            }
            else { return false; }
        }
    });
    $(".PhoneNum").text(function (i, text) {
        // returns (999) 555-1234 from 9995551234
        return text.replace(/(\d{3})?(\d{3})(\d{4})$/,
            function ($0, $1, $2, $3) {
                var formatted = ""; if ($1) { formatted += "(" + $1 + ") "; } if ($2) { formatted += $2 + "-"; } if ($3) { formatted += $3; } return formatted;
            }
        );
    });
}
function letterPos(val) {
    var alphaMap = { 'ABC': '2', 'DEF': '3', 'GHI': '4', 'JKL': '5', 'MNO': '6', 'PQRS': '7', 'TUV': '8', 'WXYZ': '9' };
    var number = "";
    $.each(alphaMap, function (key, value) {
        if (key.toLowerCase().indexOf(val.toLowerCase()) > -1)
        { number = value; }
    });
    return number;
}

Here is how it is consumed in the view form:

<div class='PhoneNum'>9995551234</div>
<input id="ContactPhone" class="PhoneSelection nextField" type="text" placeholder="Phone" value="9995551234"></input>

Results on display:

(999) 555-1234

\$\endgroup\$
  • \$\begingroup\$ I can't think of any situation where converting alphabet characters into digits would improve the user's experience. Block non-digits if you must, but don't convert them into something else. Aren't people even allowed to put spaces between numbers? What if they have to add an extension number? (Also, you should be using <input type="tel" ...> for phone numbers.) \$\endgroup\$ – squeamish ossifrage Sep 10 '15 at 20:28
  • \$\begingroup\$ Thanks. No spaces allowed on entry. But will allow 555-CODE and it immediately converts the letters to the numbers to be saved in the DB. Extensions are in another input. The entry is done by well trained inhouse end users, not the general public (haha). The type=tel caused issues in iPad Safari \$\endgroup\$ – AdamRoof Sep 10 '15 at 21:32
  • \$\begingroup\$ For example, suppose you type in 1-800-CALL-SAUL and the input field comes up with 180022558285. In terms of visual feedback, this is completely useless. You're not only displaying different characters from what was typed in, but the number of characters is also different. And on top of that, your chance of detecting the typo in the ninth digit is almost zero. I'm sorry, but this whole idea is just ghastly. You would be better off sorting out your issues with iPads than going down this route. \$\endgroup\$ – squeamish ossifrage Sep 10 '15 at 21:55
  • \$\begingroup\$ Some people prefer digits to letters on the phone. Making this completely useful! The input is only shown when editing. On save, the div displays the formatted number for the end user enjoyment. \$\endgroup\$ – AdamRoof Sep 10 '15 at 22:37
  • \$\begingroup\$ I have addressed the iPad issue by presenting my own numeric keypad instead of having the large keyboard appear in the wrong layout, but that's another topic ;) \$\endgroup\$ – AdamRoof Sep 10 '15 at 22:47
1
\$\begingroup\$

Here are some points regarding your code:

  • Decide on a style regarding the if-statements, and stick to it. Don't collapse else statements like the } else { return false; }
  • Avoid multiple statements on one line, like the var formatted = ...
  • When both branches does the same, extract the return outside of it
  • You can inline regexp's when they are simple, (see Detect numbers or letters with jquery/javascript)
  • Additionaly when using regexp against a single key you don't need to use anchors or multiple match options. Instead of ^[0-9]+$, simply use [0-9]
  • When replacing text you can enter the replacement text directly, instead of using a function. And the function you use is somewhat complicated
  • Edit: Switched to a simpler letterToNumber() using the lowercase character code (with the code of a subtracted) to index into a numbered variant of the alphabet.

In this jsfiddle I've corrected for my comments:

$(".PhoneSelection").keypress(function (e) {
    // Only allow number entry
    var key = String.fromCharCode(!e.charCode ? e.which : e.charCode);

    if (!/[0-9]/.test(key)) {
        // If not a number, is it a letter?
        if (/[A-za-z]/.test(key)) {
            // Convert the letter typed to the number on the phone keypad
            $(this).val($(this).val() + letterToNumber(key));
        }
        return false;
    }
});

$(".PhoneNum").text(function (i, text) {
    // returns (999) 555-1234 from 9995551234
    return text.replace(/(\d{3})?(\d{3})(\d{4})$/, "($1) $2-$3")
        .replace(/\(\) /, "");
});

function letterToNumber(val) {
   charIndex = val.toLowerCase().charCodeAt(0) - 97;
   return "22233344455566677778889999"[charIndex];
}

However there are still some comments to be made:

  • To remove illegal characters, i.e. non-letters, and transforming letters to numbers, whilst you are typing is frowned upon in some camps. It would be better to present a normalised number next to the input field, or after it has been submitted
  • The transformation of the input also removes the visual feedback of whether the user has entered the correct telephone number
  • Your code as it stands does not connect the input field and the output field, which might be by design, but it threw me of whilst testing it for a while
  • You are bypassing backspacing and/or movement of cursor. As an example start entering a number using letters, i.e. xxjjjmmmm, and then you realise that you forgot the first x, so you move the cursor over there and enter it x. Now the code will add that at the end, bummer...

Edit: In original research I found Code golf: Letters in phone numbers and How to convert an alphanumeric phone number to digits), where the shortest, not very readable version of alternateLetterToNumber() is (with addition of lowercasing the character):

function alternateLetterToNumber(y) {
    y = y.toLowerCase().charCodeAt(0) - 91;
    return y > 27 ? 9 : y > 24 ? 8 : y > 20 ? 7 : ~~ (y / 3);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.