The 'tel' Input Type

HTML5 introduced several text-like inputs like: email and url that perform out-of-the-box validation on the submitted value.

But be careful! One of those new types, the tel (for telephone) type, does not perform any validation. In fact, the type='tel' input is functionally no different from the type='text' input type!

This makes sense because telephone numbers vary wildly depending on what part of the world you're coming from. Therefore, the tel input type does zero input validation. You have to do it yourself.

So, let's use the pattern attribute to perform our validation:

NANP

We're going to build a regex pattern that will validate a 10-digit NANP (North-American Number Plan) telephone number. The NANP format has several rules that we're not going to attack at the moment. We'll just focus on basic validation: does the number have roughly the right "shape."

For those of us familiar with NANP, the numbers follow the format:

NPA NXX XXXX

Where:

  • NPA - area code (3 digits)
  • NXX - exchange code (3 digits)
  • XXXX - line number (4 digits)

Again, do note that there are other rules that make the NANP a little more complex than what I've described.

Those of us in North America are usually used to three formats for rendering these numbers. They are roughly:

NXX-XXXX - this is the basic 7-digit rendering of the telephone number. I think this rendering is becoming obsolete.

(NPA) NXX-XXXX - notice the parens around the area code. Also a space is used after the area code.

NPA-NXX-XXXX - this common format separates all parts of the NANP by dashes (-)

NPANXXXXXX - for the lazy among us we should be able to support this format and…

NPA NXX XXXX - this format for the very slightly less lazy among us

Let's build a pattern that supports all these common formats.

Input Validation Regexes

In plain English we want a validation rule that:

  1. allows the existence or non-existence of the NPA portion of the NANP (the area code).
  2. allows the NPA portion of the NANP to be enclosed by parens ()
  3. allows the NPA portion of the NANP to be followed by a trailing space, or dash(-) or nothing at all
  4. allows the NXX and XXXX portion of the NANP to be optionally separated by a trailing space, dash(-) or nothing at all.

Here's the regex that I came up with:

(?:\(?\d{3}\)?)?[ -]?\d{3}[ -]?\d{4}

Accept the presence or Absence of Area-Code (NPA)

So, this thing: (?:(?\d{3})?)?[ -]?

does several things:

  1. (?:(?\d{3})?)? - we use a non-capturing group to accept the possible presence of the area-code surrounded by optional parens
  2. [ -]? - we accept either a space, a dash(-) or nothing at all

You can see that I judiciously use the ? operator which means: "none or one of the previous thing."

Accept the NXX (exchange code)

This thing: \d{3}[ -]?

says:

  1. \d{3} - we simply accept exactly 3 digits
  2. [ -]? - we accept the presence of non-existence of either a space or a dash(-)

Accept the subscriber or line number

Finally, this thing: \d{4} simply accepts exactly 4 digits.

Notes

Do note that technically the NANP is more complex than what we've describe above. For example, the first digit of the area-code (NPA) should not begin with a 1.

So, we could adapt our regex to look like this to be more accurate:

(?:\(?[2-9]\d{2}\)?)?[ -]?\d{3}[ -]?\d{4}

Notice that we've replaced the first digit of \d with [2-9]. This forces the first digit of the area code to not be a 0 or 1.

Further Resources

If you want to see other useful patterns check this out.