在JavaScript中使用电话号码

从用户收集数据时,存在两个主要挑战; 收集并验证信息。 某些类型的信息很简单-例如,某人的年龄实际上并不太容易收集和验证。 名称并不像听起来那样简单,但可以满足特殊情况和国际变化的需要(例如,赞助人,专名或什至只是带有姓氏的人),您不会做错太多(尽管有很多应用服务呢!)。 电子邮件地址虽然在理论上很容易验证,但也面临着挑战-然而,仍然有很多正则表达式不太正确。

然后是电话号码。 这些很难。 真的很难。 在本文中,我将讨论有关收集,验证和显示电话号码的一些挑战。

为什么电话号码不同

也许您在想,由于电话号码通常遵循严格的格式,例如:

202-456-1111

…构造一个简单的正则表达式来验证它们应该很简单。 实际上,这是一个:

^(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}$

好吧,就在那儿停下来。 对于初学者来说,这只是上面数字的一些变体,所有这些都完全有效:

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

因此,基于此,我们知道正则表达式方法并不像我们最初想到的那么简单-但这只是其中的一半。 这些示例仅适用于美国的电话号码。 当然,如果您知道要收集的号码将用于特定国家/地区,则可以使用正则表达式。 否则,这种方法将无法实现。

让我们看一下有关电话号码的其他一些问题,以及它们为什么使我们的工作更加困难。

号码变更

各种外部因素都可能对电话号码产生影响。 整个国家来来往往,引入了新的国家前缀。 新的数字分类引入了新的编号系统-溢价率,本地费率,免费电话等。 当运营商用完一组数字(例如,可悲的是溢价)时,它们只是引入了一个新的前缀。

一些变化具有巨大的影响。 例如,几年前在英国,整个区域编号系统发生了翻天覆地的变化,几乎每个区号都插入了额外的“ 1”。 即使到那时,首都的系统也有所不同。 在全国改变标识以反映变化的大概十年。

然后,当然,移动技术有了前所未有的巨大增长。 所需的电话号码不再主要限于家庭数目,而是许多次。 可用数字池的持续紧张只会增加进一步变化的可能性。

国际拨号代码

捕获号码的国际拨号代码通常很重要。 在某些情况下,上下文可能意味着它们不是必需的。 例如,如果您在单个国家/地区开展业务,而电话号码被捕获以供操作员使用,则可能不需要它们。 但是对于任何远程自动化的工具(例如发送SMS消息)或要对其进行有效验证,则需要捕获国家/地区前缀。

国家图书馆包含大量的地理信息,其中包括国际拨号代码。 这是该库的countries.json的摘录:

{
    "name": {
        "common": "Austria",
        "official": "Republic of Austria",
        // ... //
    },
    // ... //
    "callingCode": ["43"],
    // ... //
},

如您所见,这表明奥地利使用国际拨号代码43。

那么我们将如何使用这些信息? 好吧,利用Lodash(或Underscore)的魔力,我们可以通过几种方式查询与拨号代码相关的信息。

例如,要确定给定的拨号代码是否有效:

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

    }
    // ...
}

当然,有更有效的方法可以执行此操作,因此此示例和以下示例不一定针对生产进行优化。

我们可以查找使用特定拨号代码的国家/地区:

/**
 * 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');

}

最后,我们可以获得给定国家/地区的拨号代码:

/**
 * 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;

}

您将在本文随附的存储库中找到这些功能以及单元测试,它们打包在一起作为模块。

但是,即使是国际拨号代码也没有您想像的那么简单。 格式可以有所不同– 1,43,962 1868均为有效代码。 不一定是一对一的映射。 例如,44不仅用于英国,而且用于马恩岛,根西岛和泽西岛。

号码也必须根据您拨号的位置进行更改。 在国外,要拨打英国号码,您需要删除前导零,并在拨号代码44之前添加前缀:

020 7925 0918

…成为…

+44 20 7925 0918

您也可以将“ +”替换为双零:

0044 20 7925 0918

要复杂的事情,甚至进一步,一些数字取决于你拨号哪个国家从国家之外调用时会有所不同。 例如,在美国,数字还必须带有美国出口代码011前缀,因此上面的示例变为:

011 44 20 7925 0918

值得庆幸的是,我们可以使用一种格式来解决这些变化。

E.164

幸运的是,对于开发人员来说,世界上任何地方都有一个明确的国际公认的电话号码标准,称为E.164。 格式细分如下:

  • 一个电话号码最多可以包含15位数字
  • 电话号码的第一部分是国家/地区代码
  • 第二部分是国家目的地代码(NDC)
  • 最后一部分是订户号(SN)
  • NDC和SN统称为国家(重要)号码

来源

这是E.164格式的较早编号:

+12024561111

例如,我们可以对伦敦的英国号码使用相同的格式:

+442079250918

我们可以使用E.164格式表示任何有效的电话号码。 我们知道它指的是哪个国家,而且它并不陌生-使其成为理想的存储选择。 它也常用于基于电话的服务,例如SMS提供商,我们将在稍后介绍。

当然有收获。 E.164标准可能非常适合存储,但是对于两件事却很糟糕。 首先,几乎没有人会以这种格式输入或读出他们的电话号码。 其次,就可读性而言,它毫无希望。 不过,稍后,当我们查看libphonenumber ,我们会看到有一些格式化人类的方法。

收集电话号码

不过,首先让我们看一下收集电话号码的问题。

HTML5和“电话”输入

HTML5引入了一种新的“电话”输入类型。 但是,由于围绕格式变化的问题,它实际上并未对用户可以输入的内容施加任何限制,也没有以与email元素相同的方式执行任何验证。 尽管如此,还是有一些优势–在移动站点上使用时,通常会显示用户的电话键盘,而不是传统的键盘布局。

您可以使用单个元素来收集数字:

<input type="tel" name="number">

另外,您可以将数字分解为单独的元素:

<!-- 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">

浏览器支持相当不错(例如Chrome 6 +,Firefox 4 +,Safari 5 +,IE 10+),但是即使在较旧的浏览器中,它也只会退回到纯文本字段。

如果我们确定一个正则表达式就足够了(并记住存在问题),那么我们可以使用pattern属性添加一些验证:

<input type="tel" name="number" pattern="^(?:\(\d{3}\)|\d{3})[- ]?\d{3}[- ]?\d{4}$">

屏蔽输入

屏蔽输入是一种常见的技术,用于限制用户输入或提供有关预期格式的提示。 但是,再次重申,除非您确信数字将始终针对特定国家/地区,否则很难迎合国际差异。 但是,通过做出假设来烦扰用户是一回事–要求非美国用户提供州和邮政编码。 使表格完全不可用是另一回事,例如,强迫人们以某个国家的格式提供数字。

但是,如果您知道某些数字将在特定范围内,它们将是有效的。 这是美国电话号码的掩码输入示例。

更好的方法

以出色的jQuery插件的形式,有一种更好,更灵活的方式来收集电话号码。 如下图所示。

实际使用的插件

您也可以在此处进行现场演示。

用法很简单-确保已包含jQuery,库和CSS文件,并且标志sprite可用并且可以从CSS中正确引用-您可以在build/img/flags.png找到它。

接下来,创建一个元素:

<input type="tel" id="number">

最后,将其初始化如下:

$("#number").intlTelInput();

有关配置选项的完整列表, 请参阅文档 。 稍后,我们将研究utilsScript选项,但首先,我们需要深入研究另一个有用的库。

引入libphonenumber

幸运的是,有许多解决方案可以解决我们的验证和格式化问题。 Google的libphonenumber库最初是为Android操作系统开发的,它提供了用于处理电话号码的各种方法和实用程序。 更好的是,它已从Java移植到Javascript,因此我们可以在Web或Node.js应用程序中使用它。

安装

您可以从项目主页上(如您期望的那样)从Google Code下载该库。

您也可以通过npm获取它。 这是项目页面 ,可以从命令行安装:

npm install google-libphonenumber

您也可以使用Bower安装它:

bower install libphonenumber

如果您正在考虑在前端项目中使用它,请注意-即使经过压缩和压缩,它的价格也超过200Kb。

解析数字

为了演示该库的关键功能,我假设您正在编写一个Node.js应用程序。 您可以在存储库中找到一些示例代码,作为对本文的补充

首先,导入phoneUtil

var phoneUtil = require('google-libphonenumber').phoneUtil;

现在,您可以使用其parse()方法来解释电话号码:

var tel = phoneUtil.parse('+12024561111');

我们可以做很多事情。 首先让我们从库中导入一些常量。 将您的require声明更改为以下内容:

var phoneUtil = require('google-libphonenumber').phoneUtil
        , PNF = require('google-libphonenumber').PhoneNumberFormat
        , PNT = require('google-libphonenumber').PhoneNumberType;

现在,我们可以执行以下操作:

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

输出如下:

+1 202-456-1111
(202) 456-1111
+12024561111

现在尝试在不使用国际拨号代码的情况下解析号码:

var tel = phoneUtil.parse('2024561111');

这将引发以下异常:

Error: Invalid country calling code

这是因为,如果没有明确告知电话号码所代表的国家/地区,就无法解释。 parse()方法采用可选的第二个参数,它是ISO 3166-1 alpha-2(即两个字符)国家/地区代码。

如果您再次尝试该行,但是这次传递“ US”作为第二个参数,则会发现结果与以前一样:

var tel = phoneUtil.parse('2024561111', 'US');

您还可以尝试各种格式; 所有这些也将起作用:

var tel = phoneUtil.parse('202-456-1111', 'US');
var tel = phoneUtil.parse('(202) 456 1111', 'US');

解释英国号码:

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

这将输出以下内容:

+44 20 7925 0918
020 7925 0918
+442079250918

解析数字后,就可以对其进行验证-正如我们将在下一部分中看到的那样。

验证号码

验证遵循类似的模式; 同样,还有第二个可选参数,但是如果未明确说明国家/地区,则需要使用该参数。

以下是一些有效数字的示例,其中国家/地区代码或者作为第二个参数提供,或者包含在第一个参数中:

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

如果您不提供国家代码,或者没有提供国家代码,则将得到与以前相同的错误:

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"

以下是验证失败并返回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)

但是请注意,因为无效数字可能会引发异常:

console.log(phoneUtil.isValidNumber(phoneUtil.parse('NOT-A-NUMBER', 'US')));
// => throws exception "Error: The string supplied did not seem to be a phone number"

确定数字的类型

有时,了解电话号码的类型很有用。 例如,您可能希望确保获得一个手机号码-也许您打算发送SMS消息(例如,实施两因素身份验证)或尝试淘汰特优费率号码。

库的getNumberType()函数就是这样做的。 让我们来看看。

该函数将已解析的电话号码作为其参数:

var tel = phoneUtil.parse('(0) 20 7925 0918', 'GB');
var type = phoneUtil.getNumberType(tel)

返回值是在PhoneNumberType子模块中定义的常量–您会记得我们require将此输入为PNF。

例如,让我们查询所讨论的数字是移动电话还是固定电话:

if (type === PNT.MOBILE) {
    console.log("It's a mobile number");
} else if (type === PNT.FIXED_LINE) {
    console.log("It's a fixed line");
}

似乎是该主题的主题,自然有一个问题。 有时,甚至libphonenumber库也无法确定。 例如,美国号码很难区分。 因此常量PNT.FIXED_LINE_OR_MOBILE

我们只需要更改示例代码即可反映这种不确定性:

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

也有许多其他可能性。 这是当前的完整列表:

  • 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

如您所见, PNT.UNKNOWN反映了一个事实,即我们不一定可以肯定地收集任何信息。 因此,总而言之,尽管此功能可以用作快速的初始检查,但我们不能依靠它。

号码在服务中吗?

有很多电话号码可以验证,但尚未使用。 它们可能已断开连接,尚未分配,或者SIM卡已掉落在厕所中。

如果您需要确保一个数字不仅有效而且有效,那么您可以使用许多选项。

一种方法是要求用户确认其号码,与您可能要求用户确认其电子邮件地址的方式几乎相同。 您可以使用Twilio之类的服务来发送SMS ,甚至可以拨打电话

这是一个非常简单的代码片段,用于使用Twilio通过SMS生成和发送确认代码:

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

然后,要求用户在您的Web应用程序中将代码输入表单中进行验证是一件很简单的事情,或者您甚至可以允许人们通过回复消息来验证其号码。

还有(收费)服务,将实时检查您是否正在使用某个号码,例如Byteplant的服务

其他事宜

与任何个人信息一样,也要注意很多法律问题。 例如,在英国, 电话优先服务(TPS)是电话号码的国家注册簿,该电话簿已由不希望接收市场营销通讯的人明确注册。 有一些付费服务提供API来根据此寄存器检查数字,例如this

可用性注意事项

以一种形式请求多达三个不同的电话号码是很常见的。 例如白天,晚上和移动。

还值得记住的是,在互联网上询问电话号码可能很麻烦。 如果有人尽管您已将其设置为必填字段而仍不愿意提供该信息,则他们可能会执行以下两项操作之一:

  • 尝试“欺骗”验证。 根据方法的不同,他们可能会键入“ ex directory”之类的内容,或输入无效的数字-例如仅包含数字的数字。
  • 走开。

结合jQuery插件和libphonenumber

您可能还记得,jQuery插件有一个名为utilsScript的相当隐秘的名称选项。

此选项使我们可以利用libphonenumber的验证和格式化功能。 选择一个国家(使用下拉列表或输入拨号代码)后,它将把文本字段转换为可反映该国家编号格式的带掩码输入。

该插件包含libphonenumber的打包版本。 将此文件的路径传递给构造函数,如下所示:

$("#number").intlTelInput(
    {
        utilsScript : '/bower_components/intl-tel-input/lib/libphonenumber/build/utils.js'
    }
);

如前所述,请记住,由于libphonenumber库的文件大小,应谨慎使用此方法。 但是,在构造函数中在此处引用它确实意味着可以按需加载它。

显示电话号码

我们已经研究了如何使用PNF.INTERNATIONALPNF.NATIONAL格式显示数字时使其更“友好”。

我们还可以使用telcallto协议在电话号码上添加超链接,这在移动网站上特别有用-允许用户直接从网页拨打号码。

为此,我们需要链接本身采用E.164格式-例如:

<a href="tel:+12024561111">+1 202-456-1111</a>

当然,您可以使用libphonenumber库的format()方法来呈现E.164版本( PNF.E164 )和更加用户友好的显示版本。

微数据

我们还可以使用微数据在语义上标记电话号码。 这是一个例子。 请注意使用itemprop="telephone"标记链接:

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

摘要

在本文中,我们打开了黄蜂巢,即电话号码。 到现在为止,应该很明显地知道,如果需要收集,验证和显示它们,则需要了解各种复杂性,微妙性和陷阱。

我们已经研究了几种收集数字的方法-“ tel”输入类型,带掩码的输入,最后是intl-tel-input jQuery插件。

然后,我们研究了围绕验证的一些问题,以及为什么常规方法(例如正则表达式)常常不足,尤其是在您走出国门时。

我们看了一下Google的libphonenumber库; 使用它来解析,验证,显示和确定电话号码的类型。

我们将intl-tel-input插件与libphonenumber ,以提供更好的用户体验,尽管这是以性能为代价的。

最后,我们研究了如何在HTML中标记电话号码。

对于处理电话号码,我将提出一些建议:

  • 除非您仅在一个国家/地区开展业务,否则请注意国际差异。
  • 请谨慎使用带掩盖的输入。
  • 基于正则表达式的验证要非常小心。
  • 尽可能使用E.164进行存储。
  • 使用Google的libphonenumber库。
  • 显示数字时,请尽可能设置其格式,使用tel:或callto:链接类型,然后使用微数据。

From: https://www.sitepoint.com/working-phone-numbers-javascript/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值