When you’re collecting data from users, there are two key challenges; collecting that information, and validating it. Some types of information are straightforward – someone’s age, for example, couldn’t really be simpler to collect and to validate. Names aren’t as straightforward as they sound, but provided you cater for edge cases and international variations – for example patronymics, the mononymous, or even just people with hyphenated surnames – you can’t go too far wrong (although plenty of applications and services do!). Email addresses, while theoretically very easy to validate, have their own challenges – yet nevertheless, there are plenty of regular expressions in the wild that aren’t quite right.
And then there are telephone numbers. These are hard. Really hard. In this article I’ll discuss some of the challenges around collecting, validating, and displaying telephone numbers.
Why Telephone Numbers are Different
Perhaps you’re thinking that since telephone numbers tend to follow a pretty rigid format, such as this:
202-456-1111
…that it ought to be simple to construct a simple regular expression to validate them. In fact, here’s one:
^(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$
Well, stop right there. For starters, here are just some variations of the number above, all of which are perfectly valid:
202 456 1111
(202) 456 1111
2024561111
1-202-456-1111
1-202-456-1111 x1234
1-202-456-1111 ext1234
1 (202) 456-1111
1.202.456.1111
1/202/456/1111
12024561111
+1 202 456 1111
So based on that, we know that the regular expression apparoach isn’t as simple as we first thought – but that’s only the half of it. These examples are just for a US-based number. Sure, if you know that the number you’re collecting is going to be for a specific country, you may be able to use a regular expression. Otherwise, this approach won’t cut it.
Let’s look at some of the other issues around telephone numbers, and why they make our job even harder.
Numbers Change
All sorts of external factors can have implications for telephone numbering. Whole countries come and go, introducing new country prefixes. New classifications of numbers introduce new numbering systems – premium-rate, local-rate, toll-free, and so on. When a carrier runs out of one set of numbers – like, sadly, premium-rate – they simply introduce a new prefix.
Some changes have enormous implications; in the United Kingdom some years ago, for example, the entire regional numbering system underwent a drastic change, with virtually every area code getting an additional “1” inserted. Even then, the capital had a subtly different system. It was probably a decade before signage was changed across the country to reflect the changes.
Then, of course, there was the enormous and unprecedented growth in mobile. No longer was the number of telephone numbers required largely limited to the number of households, but many times over. The continued strain on the pool of available numbers can only increase the likelihood of further changes.
International Dialing Codes
It’s often important to capture a number’s international dialing code. In some cases, the context might mean they aren’t required. For example if you operate in a single country, and telephone numbers are captured to be used by a human operator, you might not need them. But for anything remotely automated – such as sending SMS messages – or to validate them effectively, you’ll need to capture the country prefix.
The countries library contains a bunch of geographical information which includes international dialing codes. Here is an excerpt from countries.json
from that library:
{
"name": {
"common": "Austria",
"official": "Republic of Austria",
// ... //
},
// ... //
"callingCode": ["43"],
// ... //
},
As you can see, this demonstrates that Austria uses the international dialing code 43.
So how might we use this information? Well, using the magic of Lodash (or Underscore), there are a few ways in which we can query dialing code-related information.
For example, to find out whether a given dialing code is valid:
var _ = require('lodash')
, data = require('world-countries')
module.exports = {
/**
* Determines whether a given international dialing code is valid
*
* @param string code
* @return bool
*/
isValid : function(code) {
var codes = _.flatten(_.pluck(data, 'callingCode'));
return _.contains(codes, code);
}
// ...
}
There are a more efficent ways of doing this, of course, so this and the following examples aren’t necessarily optimized for production.
We can look up the countries which use a particular dialing code:
/**
* Gets a list of countries with the specified dialing code
*
* @param string code
* @return array An array of two-character country codes
*/
getCountries : function(code) {
var countryEntries = _.filter(data, function(country){
return (_.contains(country.callingCode, code));
})
return _.pluck(countryEntries, 'cca2');
}
Finally, we can get the dialing codes for given country:
/**
* Gets the dialing codes for a given country
*
* @param string country The two-character country code
* @return array An array of strings representing the dialing codes
*/
getCodes : function(country) {
// Get the country entry
var countryData = _.find(data, function(entry) {
return (entry.cca2 == country);
});
// Return the code(s)
return countryData.callingCode;
}
You’ll find these functions packaged up as a module, along with unit tests, in the repository that accompanies the article.
Even international dialing codes, however, aren’t as straightforward as you may think. The format can vary – 1, 43, 962 1868 are all valid codes. There isn’t necessarily a one-to-one mapping; 44 for example, is used not just for the United Kingdom but for the Isle of Man, Guernsey and Jersey.
Numbers must also be altered according to where you’re dialing from. From abroad, to call a UK number you need to drop the leading zero and prefix with the dialing code 44:
020 7925 0918
…becomes…
+44 20 7925 0918
You can also replace the “+” with a double zero:
0044 20 7925 0918
To complicate things even further, some numbers vary when called from outside of a country depending on which country you’re dialing from. In the US, for example, numbers must also be prefixed with the US exit code 011
, so the example above becomes:
011 44 20 7925 0918
Thankfully, there is a format we can use which enable us to get around these variations.
E.164
Luckily for developers there is an unambiguous, internationally recognized standard for telephone numbers anywhere in the World called E.164. The format is broken down as follows:
- A telephone number can have a maximum of 15 digits
- The first part of the telephone number is the country code
- The second part is the national destination code (NDC)
- The last part is the subscriber number (SN)
- The NDC and SN together are collectively called the national (significant) number
(source)
Here’s the number from earlier, in E.164 format:
+12024561111
We can use the same format for, as an example, a London-based UK number:
+442079250918
We can represent any valid telephone number using the E.164 format. We know what country it refers to, and it’s unabmiguous – making it the ideal choice for storage. It’s also commonly used for telephony based services such as SMS providers, as we’ll see a little later.
There’s a catch, of course. The E.164 standard might be great for storage, but terrible for two things. First, virtually no one would type or read out their number in that format. Second, it’s hopeless in terms of its readability. Later though, when we look at libphonenumber
, we’ll see that there are ways of formatting numbers for humans.
Collecting Telephone Numbers
First though, let’s look at the issue of collecting telephone numbers.
HTML5 and the “tel” input
HTML5 introduced a new “tel” input type. However, because of the issues around the variations in format, it doesn’t actually place any restrictions on what the user can type, nor does it perform any validation in the same way as, say, the email element. Nevertheless, there are some advantages – when used on a mobile site a user’s telephone keypad will usually be displayed, rather than a conventional keyboard layout.
You can use a single element to collect a number:
<input type="tel" name="number">
Alternatively, you can break a number down into separate elements:
<!-- area code and number -->
<input type="tel" name="number">
<!-- country code, area code and number -->
<input type="tel" name="country" size="4"> <input type="tel" name="area" size="6"> <input type="tel" name="number" size="8">
<!-- US-style -->
(<input type="tel" size="3">) <input type="tel" size="3"> - <input type="tel" size="4">
Browser support is pretty good (e.g. Chrome 6+, Firefox 4+, Safari 5+, IE 10+), but even in an older browser it will simply fall back to a plain old text field.
Should we decide that a regular expression is sufficient – and remember, there are issues – then we can use the pattern
attribute to add some validation:
<input type="tel" name="number" pattern="^(?:\(\d{3}\)|\d{3})[- ]?\d{3}[- ]?\d{4}$">
Masked Inputs
Masked inputs are a common technique for restricting user input or providing hints as to the expected format. But again, unless you can be confident that numbers will always be for a particular country, it’s very difficult to cater to international variations. However, it’s one thing to annoy users by making assumptions – asking a non-US user to provide a state and a zip-code. It’s quite another to make a form completely unusable, for example by forcing people to provide numbers in a certain country’s format.
Nevertheless, they can be effective if you know that certain numbers will be within a particular range. Here is an example of a masked input for US telephone numbers.
A Better Way
There is a better and more flexible way to collect telephone numbers, in the form of an excellent jQuery plugin. It’s illustrated below.
You can also play with a live demo here.
Usage is simple – make sure you’ve included jQuery, the library, and the CSS file, and that the flag sprite is available and properly referenced from the CSS – you’ll find it in build/img/flags.png
.
Next, create an element:
<input type="tel" id="number">
Finally, intialize it as follows:
$("#number").intlTelInput();
For a full list of configuration options, consult the documentation. Later, we’ll look at the utilsScript
option, but first, we need to delve into another useful library.
Introducing libphonenumber
Luckily, there’s a solution to many of our validation and formatting woes. Originally developed for the Android operating system, Google’s libphonenumber library offers all sorts of methods and utilities for working with telephone numbers. Better still, it’s been ported from Java to Javascript, so we can use it in web or Node.js applications.
Installation
You can download the library from the project homepage on – as you might expect – Google Code.
You can also get it via npm. Here’s the project page, and to install from the command-line:
npm install google-libphonenumber
You can also install it using Bower:
bower install libphonenumber
If you’re thinking of using it in a front-end project, be warned though – even when minified and compressed, it comes in at over 200Kb.
Parsing Numbers
In order to demonstrate the library’s key features, I’m going to assume you’re writing a Node.js application. You can find some example code in the repository which complements this article.
First, import phoneUtil
:
var phoneUtil = require('google-libphonenumber').phoneUtil;
Now you can use its parse()
method to interpret a telephone number:
var tel = phoneUtil.parse('+12024561111');
There are a number of things we can do with this. Let’s first import some constants from the library. Change your require
declaration to the following:
var phoneUtil = require('google-libphonenumber').phoneUtil
, PNF = require('google-libphonenumber').PhoneNumberFormat
, PNT = require('google-libphonenumber').PhoneNumberType;
Now we can do the following:
var tel = phoneUtil.parse('+12024561111');
console.log(phoneUtil.format(tel, PNF.INTERNATIONAL));
console.log(phoneUtil.format(tel, PNF.NATIONAL));
console.log(phoneUtil.format(tel, PNF.E164));
The output from this will be as follows:
+1 202-456-1111
(202) 456-1111
+12024561111
Now try parsing the number without the international dialing code:
var tel = phoneUtil.parse('2024561111');
This will throw the following exception:
Error: Invalid country calling code
This is because without explictly telling it what country the number is for, it’s impossible to interpret. The parse()
method takes an optional second parameter, which is the ISO 3166-1 alpha-2 (i.e., two character) country code.
If you try the line again, but this time passing “US” as the second argument, you’ll find that the results are as before:
var tel = phoneUtil.parse('2024561111', 'US');
You can also play around with the formats; all of these will work, too:
var tel = phoneUtil.parse('202-456-1111', 'US');
var tel = phoneUtil.parse('(202) 456 1111', 'US');
To interpret a United Kingdom number:
var tel = phoneUtil.parse('(0) 20 7925 0918', 'GB');
console.log(phoneUtil.format(tel, PNF.INTERNATIONAL));
console.log(phoneUtil.format(tel, PNF.NATIONAL));
console.log(phoneUtil.format(tel, PNF.E164));
This will output the following:
+44 20 7925 0918
020 7925 0918
+442079250918
Once you’ve parsed a number, you can validate it – as we’ll see in the next section.
Validating a Number
Validation follows a similar pattern; again, there is a second optional argument, but one which you’re going to need if the country isn’t implictly stated.
Here are some examples of valid numbers, where the country code is either provided as the second argument, or contained within the first argument:
console.log(phoneUtil.isValidNumber(phoneUtil.parse('+12024561111')));
// => outputs true
console.log(phoneUtil.isValidNumber(phoneUtil.parse('202-456-1111', 'US')));
// => outputs true
console.log(phoneUtil.isValidNumber(phoneUtil.parse('(0) 20 7925 0918', 'GB')));
// => outputs true
If you don’t supply the country code, or it’s not implied, you get the same error as before:
console.log(phoneUtil.isValidNumber(phoneUtil.parse('(0) 20 7925 0918')));
// => throws exception "Error: Invalid country calling code"
console.log(phoneUtil.isValidNumber(phoneUtil.parse('2024561111')));
// => throws exception "Error: Invalid country calling code"
Here are some examples where validation fails, returning false
:
console.log(phoneUtil.isValidNumber(phoneUtil.parse('573 1234 1234', 'US')));
// => outputs false
console.log(phoneUtil.isValidNumber(phoneUtil.parse('555-555-5555', 'US')));
// => outputs false (this is often used as a placeholder, but it's not a valid number)
console.log(phoneUtil.isValidNumber(phoneUtil.parse('295-123-1234', 'US')));
// => outputs false (there is no 295 area code in the US)
Be warned, however, as an invalid number can throw an exception:
console.log(phoneUtil.isValidNumber(phoneUtil.parse('NOT-A-NUMBER', 'US')));
// => throws exception "Error: The string supplied did not seem to be a phone number"
Determining a Number’s Type
Sometimes, it’s useful to know the type of a telephone number. For example, you may wish to ensure that you’ve been provided with a mobile number – perhaps you plan to send SMS messages, for example to implement two-factor authentication – or attempt to weed out premium rate numbers.
The library’s getNumberType()
function does just that. Let’s take a look.
The function takes a parsed telephone number as its argument:
var tel = phoneUtil.parse('(0) 20 7925 0918', 'GB');
var type = phoneUtil.getNumberType(tel)
The return value is a constant defined in the PhoneNumberType sub-module – you’ll recall we’ve require
‘d this in as PNF.
As an example, let’s query whether the number in question is a mobile or a fixed line:
if (type === PNT.MOBILE) {
console.log("It's a mobile number");
} else if (type === PNT.FIXED_LINE) {
console.log("It's a fixed line");
}
As seems to be the theme of the topic, naturally there’s a catch. Sometimes, even the libphonenumber library can’t be sure. US numbers, for example, cannot be easily distinguished; hence the constant PNT.FIXED_LINE_OR_MOBILE
.
We’ll just have to change our example code to reflect this uncertainty:
if (type === PNT.MOBILE) {
console.log("It's a mobile number");
} else if (type === PNT.FIXED_LINE) {
console.log("It's a fixed line");
} else if (type === PNT.FIXED_LINE_OR_MOBILE) {
console.log("Your guess is as good as mine");
}
There are a number of other possibilities, too. Here is the full list currently:
PNT.FIXED_LINE
PNT.MOBILE
PNT.FIXED_LINE_OR_MOBILE
PNT.TOLL_FREE
PNT.PREMIUM_RATE
PNT.SHARED_COST
PNT.VOIP
PNT.PERSONAL_NUMBER
PNT.PAGER
PNT.UAN
PNT.UNKNOWN
As you can see, the PNT.UNKNOWN
reflects the fact that we can’t necessarily glean any information with any certaintly. So in summary, whilst this feature can be useful as a quick initial check, we can’t rely on it.
Is the Number in Service?
There are plenty of telephone numbers which will validate, but which are not in use. They may have been disconnected, not yet allocated, or perhaps a SIM card has been dropped down a toilet.
If you need to ensure that a number is not just valid but also active, there are a number of options open to you.
One approach is to require users’ confirm their number, in much the same way as you might require users confirm their email address. You can use a service such as Twilio to send an SMS, or even place a call.
Here is a very simple code snippet for generating and sending a confirmation code by SMS using Twilio:
// You'll need to install the Twilio library with "npm install twilio"
var client = require('twilio')('YOUR-SID', 'YOUR-AUTH-TOKEN');
// Generate a random four-digit code
var code = Math.floor(Math.random()*8999+1000);
// Send the SMS
client.sendMessage({
to: phoneUtil.format(tel, PNF.E164), // using libphonenumber to convert to E.164
from: 'YOUR-NUMBER',
body: 'Your confirmation code is ' + code
}, function(err, respons) {
// ...do something
});
It’s then a trivial exercise to ask users to enter the code into a form in your web app to verify it – or you could even allow people to validate their number by replying to the message.
There are also (paid) services which will check whether a number is in service for you in realtime, such as this one from Byteplant.
Other Issues
Legal
As with any personal information, there are also plenty of legal issues to be mindful of. In the UK, for example, the Telephone Preference Service (TPS) is a national register of telephone numbers which have explictly been registered by people not wishing to receive marketing communications. There are paid services which offer APIs to check a number against this register, such as this one.
Usability Considerations
It’s very common to request up to three different telephone numbers in a single form; for example daytime, evening and mobile.
It’s also worth remembering that asking for telephone numbers over the internet can come across as rather intrusive. If someone is unwilling to provide that information despite you having made it a required field, they will probably do one of two things:
- Attempt to “fool” the validation. Depending on the approach, they may type something like “ex directory”, or enter an invalid number – such as one which contains only numbers.
- Walk away.
Combining the jQuery Plugin with libphonenumber
You might remember that the jQuery plugin has a rather cryptically named option called utilsScript
.
This option allows us to take advantage of the validation and formatting features of libphonenumber
. Having selected a country – either using the drop-down or by typing the dialing code – it will transform the textfield into a masked input which reflects that country’s numbering format.
The plugin contains a packaged version of libphonenumber; pass the path to this file to the constructor as follows:
$("#number").intlTelInput(
{
utilsScript : '/bower_components/intl-tel-input/lib/libphonenumber/build/utils.js'
}
);
As I’ve previously mentioned, do bear in mind that this approach should be used with caution, owing to the file size of the libphonenumber
library. Referencing it here in the constructor does however mean that it can be loaded on demand.
Displaying Telephone Numbers
We’ve looked at how we can format numbers when displaying them to be more “friendly”, using formats such as PNF.INTERNATIONAL
and PNF.NATIONAL
.
We can also use the tel
and callto
protocols to add hyperlinks to telephone numbers, which are particulary useful on mobile sites – allowing users to dial a number direct from a web page.
To do this, we need the E.164 format for the link itself – for example:
<a href="tel:+12024561111">+1 202-456-1111</a>
Of course, you could use the libphonenumber
library’s format()
method to render both the E.164 version (PNF.E164
) and the more user-friendly display version.
Microdata
We can also use Microdata to mark up telephone numbers semantically. Here’s an example; note the use of itemprop="telephone"
to mark-up the link:
<div itemscope itemtype="http://schema.org/LocalBusiness">
<h1 itemprop="name">Acme Corp, Inc.</h1>
Phone: <span itemprop="telephone"><a href="tel:+12024561111">202-456-1111</a></span>
</div>
Summary
In this article, we’ve opened up the hornets nest that is telephone numbers. It should be pretty apparent by now that there are all sorts of complexities, subtleties and gotchas you need to be aware of if you need to collect, validate and display them.
We’ve looked at a few methods for collecting numbers – the “tel” input type, masked inputs and finally the intl-tel-input
jQuery plugin.
We then looked at some of the issues around validation, and why common approaches such as regular expressions are often inadequate, particularly when you go international.
We took a look at Google’s libphonenumber
library; using it to parse, validate, display and determine the type of telephone numbers.
We combined the intl-tel-input
plugin with libphonenumber
for an even better user experience, albeit one which comes at a cost in terms of performance.
Finally we looked at how we might mark up telephone numbers in our HTML.
There are a few recommendations I would make for dealing with telephone numbers:
- Unless you only operate in a single country, be aware of the international differences.
- Use masked inputs with caution.
- Be very careful with regular expression-based validation.
- Where possible, use E.164 for storage.
- Use Google’s libphonenumber library.
- When displaying numbers, format them where possible, use the tel: or callto: link type, and use Microdata.
FAQs About Phone Numbers in JavaScript
How to get the country code from a phone number in JavaScript?
To extract the country code from a phone number in JavaScript, you can leverage the libphonenumber library provided by Google. This library is a powerful tool for parsing, formatting, and validating phone numbers, making it an ideal choice for extracting country codes accurately. To get started, you’ll need to install the library using npm, and then import it into your JavaScript file. Once imported, you can use the PhoneNumberUtil
class to work with phone numbers effectively.
To extract the country code from a phone number, you should first parse the phone number using the parse
function. This function takes the phone number as a string and an optional default region code, which is typically the ISO 3166-1 alpha-2 country code. After parsing, you can access the PhoneNumber
object and use the getCountryCode
method to obtain the country code as an integer. This approach ensures accuracy in handling international phone numbers and takes into account various regional formats
How to validate a phone number with a country code in JavaScript?
Validating a phone number with a country code in JavaScript can be a complex task due to the various international phone number formats. To ensure accurate validation, one effective approach is to use the “libphonenumber” library by Google. This library offers comprehensive tools for parsing, formatting, and validating phone numbers, making it a reliable choice for this purpose.
To get started, you need to install the “libphonenumber” library using npm and then import it into your JavaScript file. Once imported, you can leverage the library’s PhoneNumberUtil
class to perform phone number validation. The isValidNumber
function, specifically, allows you to validate a parsed phone number, returning true
if the number is valid or false
if it’s not. By parsing the phone number using the library’s capabilities, you ensure that it adheres to the specific format and rules associated with the country code provided.
This approach provides a robust solution for validating phone numbers with country codes, offering an accurate means of handling international phone numbers with varying formats and standards. By implementing the “libphonenumber” library, you can significantly enhance the precision and reliability of phone number validation in your JavaScript applications.
How to separate the country code from a phone number in JavaScript?
Separating the country code from a phone number in JavaScript is a common task, especially when dealing with international phone numbers. A practical approach involves using regular expressions and string manipulation. You can define a regular expression pattern to match the country code, which is typically represented as a plus sign followed by one or more digits. The regular expression, such as \+(\d+)
, efficiently captures the country code from the phone number.
Once you have the regular expression pattern in place, you can use the exec
method to apply it to the phone number. The exec
method returns an array containing the matched part of the phone number and any captured groups. In this case, the captured country code can be accessed from the first group in the array (index 1). By following this process, you can accurately separate the country code from the phone number and make it available for further use in your JavaScript application.
This method is versatile and can be adapted to work with various phone number formats. Whether you’re handling user input or processing phone numbers from external sources, this approach ensures that you can reliably extract the country code, a valuable step in working with international phone numbers.
How do I format my phone number internationally?
Formatting a phone number for international use is essential to ensure clarity and compatibility across different regions. An internationally formatted phone number typically consists of three main components: the country code, the area code (if applicable), and the local phone number. The country code is represented by a plus sign (+) followed by the numeric code, such as +1 for the United States. It serves as a universally recognized identifier for the country or region.
In some cases, an area code may be included, often separated from the rest of the number with a space or a hyphen. The local phone number follows, which can vary in length and may contain additional separators or punctuation. The specific format can differ between countries, so it’s important to follow local conventions when available. By adhering to this structure, you ensure that phone numbers are easily recognizable and usable across international borders.
Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.