Javascript - 替换“点击通话”链接中​​尚未包含的所有电话号码

时间:2015-12-22 00:09:56

标签: javascript regex nested filtering

这是参考我上周发布的question

我想在页面上查找所有有效的电话号码。如果它们尚未在链接中,我想创建一个“点击呼叫”链接以在移动浏览器上使用。 我在发布的原始问题上收到了几个很好的答案,但是想尝试不同的方法/扩展反馈。

我正在使用jQuery和Regex来过滤页面内容并使链接可以点击。

以下是我的想法:

    var phoneRegex = new RegExp(/([(]?\d{3}[)]?[(\s)?.-]\d{3}[\s.-]\d{4})(?![^<]*>|[^<>]*<\/)/g);
    var phoneNums = $( "body *" ).filter(function() {
    var tagname = $(this).prop("tagName");
    tagname = tagname === null ? "" : tagname.toLowerCase(); 
    if (tagname == "a") { 
        return false;
    }
    var match = $(this).html().match(phoneRegex);
    if (match === null || match.length === 0) {
        return false;
    }
    return true;
});
phoneNums.html(function() {
    var newhtml = $(this).html().replace(phoneRegex, function(match) {
        var phonenumber = match.replace(/ /g, "").replace(/-/g, "").replace(/\(/g, "").replace(/\)/g, "");
        var link = '<a href="tel:' + phonenumber + '">' + match + '</a>';
        return link;
    });
    return newhtml;
});

所以,基本上我搜索身体中寻找每个标签的一切(不包括锚标签)。我匹配正则表达式并将值存储在'phoneNums'变量中。 从那里我删除所有空格,破折号和括号,以便数字将正确格式化tel属性。 所以像这样的数字:(123)456-7890将格式如下:<a href="tel:1234567890">(123) 456-7890</a>

我看到这样做的问题是如果这些数字是在页面上的嵌套标签中,我会多次得到结果。 (如果您在链接上执行console.log,就会在返回之前看到这一点。)结果是正确的,但想知道这是否有意义。

有更有效的方法吗?提前致谢!

1 个答案:

答案 0 :(得分:1)

和以前一样(在我更新它以包含执行元素替换的代码后,从原始问题复制粘贴)Don't use regular expressions to parse HTML。使用HTML / DOM解析器获取文本节点(浏览器可以为您过滤掉它,删除锚标签以及所有文本都太短而不能包含电话号码),您可以直接检查文本。

例如,使用XPath(有点难看,但支持直接处理文本节点,大多数其他DOM方法都没有):

// This query finds all text nodes with at least 12 non-whitespace characters
// who are not direct children of an anchor tag
// Letting XPath apply basic filters dramatically reduces the number of elements
// you need to process (there are tons of short and/or pure whitespace text nodes
// in most DOMs)
var xpr = document.evaluate('descendant-or-self::text()[not(parent::A) and string-length(normalize-space(self::text())) >= 12]',
                            document.body, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0, len=xpr.snapshotLength; i < len; ++i) {
    var txt = xpr.snapshotItem(i);
    // Splits with grouping to preserve the text split on
    var numbers = txt.data.split(/([(]?\d{3}[)]?[(\s)?.-]\d{3}[\s.-]\d{4})/);
    // split will return at least three items on a hit, prefix, split match, and suffix
    if (numbers.length >= 3) {
        var parent = txt.parentNode; // Save parent before replacing child
        // Replace contents of parent with text before first number
        parent.textContent = numbers[0];

        // Now explicitly create pairs of anchors and following text nodes
        for (var i = 1; i < numbers.length; i += 2) {
            // Operate in pairs; odd index is phone number, even is 
            // text following that phone number
            var anc = document.createElement('a');
            anc.href = 'tel:' + numbers[i].replace(/\D+/g, '');
            anc.textContent = numbers[i];
            parent.appendChild(anc);
            parent.appendChild(document.createTextNode(numbers[i+1]));
        }
        parent.normalize(); // Normalize whitespace after rebuilding
    }
}

对于记录,基本过滤器可帮助大多数页面上的批次。例如,在此页面上,正如我所看到的(根据用户,浏览器,浏览器扩展和脚本等因素而不同),如果没有过滤器,查询'descendant-or-self::text()'的快照将包含1794项。省略锚标记的父级文本,'descendant-or-self::text()[not(parent::A)]'将其降低到1538,并且验证非空白内容至少为12个字符长的完整查询将其归结为87个项目。将正则表达式应用于87个项目是笨拙的改变,性能方面,并且您已经无需使用不合适的工具解析HTML。