Такая элементарная вещь, как номер телефона, в письменных текстах живёт во множестве вариантов. Обыкновенный номер типа +7 (123) 123-45-67
можно встретить записанным без скобок или дефисов (+7 123 1234567
), а то и вообще без пробелов (+71231234567
). Не собираюсь оскорблять чувства пишущих, им и так непросто. Но уважающий себя веб-ресурс не может допустить такой типографической разношёрстности. Плюс к тому, необлагороженные номера неудобно читать человеку (то самое human-readable).
Данная статья о том, как привести все телефонные номера на странице к однообразному виду, а также проставить корректные ссылки типа tel:
на них. Для решения поставленной задачи используются регулярные выражения JavaScript. Хороший обзор регэкспов дан здесь, также почитать по теме можно на MDN.
В качестве эталонной будет принята запись номера вида +7 (123) 123-45-67
.
Найти все телефонные номера на странице можно таким способом:
let pattern = new RegExp('(\+7|8)[\s(]*\d{3}[)\s]*\d{3}[\s-]?\d{2}[\s-]?\d{2}', 'g'); // создать регулярное выражение
let phoneNumbers = document.body.innerText.match(pattern); // применить на всём тексте документа
Метод match()
вернёт и запишет в переменную phoneNumbers
массив (объект типа Array
) со всеми найденными на странице номерами.
Разберём это выражение:
(\+7|8) |
+7 (123) 123-45-67 На практике встречается начало и с 8 , и с +7 .Напомню: | означает или. То есть, на данной позиции паттерна может стоять любой из вариантов, разделённых | . Также важно не забыть поставить скобки :))
|
[\s(]*\d{3}[)\s]* |
+7 (123) 123-45-67 |
[\s(]* |
+7 (123) 123-45-67 То есть, фактически, эти части паттерна распознают первые три цифры после кода страны, причём как взятые в скобки, так и без (квантификатор * синонимичен {0,} и означает «либо ноль, либо сколько угодно»).Также напомню: \d stands for символьная группа всех цифр (всё равно что [0-9] ), \s — группа всех пробельных символов.
|
\d{3}[\s-]?\d{2}[\s-]?\d{2} |
+7 (123) 123-45-67 |
[\s-]? |
+7 (123) 123-45-67 Данная часть паттерна распознаёт последние 7 цифр номера. Они могут быть разделены дефисами, пробелами или вообще ничем. Напоминалка: квантификатор ? синонимичен {0,1} и означает «либо одно вхождение, либо ничего»).Наверное, уже нет смысла пояснять, что цифра, взятая в фигурные скобки {} , означает точное количество повторений данного символа/группы в шаблоне.
|
Ну хорошо, найти все номера получилось. К сожалению, задача этим не исчерпывается. Как же взять и заменить все вхождения разом?
Здесь поможет метод replace()
. Если у регулярного выражения указан флаг 'g'
(второй аргумент конструктора), то replace()
заменит все вхождения на то, что указано. Например, задача замены всех повторяющихся пробелов в тексте на одинарные решается таким простым способом:
text = text.replace(/\s{1,}/g, ' ');
На всякий: /\s{1,}/g
— это краткая запись для new RegExp('\s+', 'g')
. Также вместо {1,}
можно писать просто +
.
Но ведь у нас задача сложнее: нельзя же заменить разные номера телефонов на один! Тут самое время вспомнить о скобочных группах.
Получается,
К счастью, replace()
поддерживает работу с группами. Достаточно написать в шаблоне замены (второй параметр) ${номер_группы}
($1
или $5
), и функция заменит эту конструкцию на содержимое скобочной группы.
То была идея, а теперь реализация:
let pattern = /(\+7|8)[\s(]?(\d{3})[\s)]?(\d{3})[\s-]?(\d{2})[\s-]?(\d{2})/g; // паттерн с проставленными скобками
let phoneNumbers = document.body.innerText.match(pattern); // найдём все номера
let correctNumber = phoneNumbers[0].replace(pattern, '+7 ($2) $3-$4-$5'); // пробуем замену
console.log(correctNumber);
В результате будет выведен аккуратный, каноничный телефонный номер. Работает!
Теперь, чтобы заменить все найденные номера телефонов на единообразные, добавим флаг g
:
let pattern = /(\+7|8)[\s(]?(\d{3})[\s)]?(\d{3})[\s-]?(\d{2})[\s-]?(\d{2})/g;
document.body.innerHTML = document.body.innerHTML.replace(pattern, '+7 ($2) $3-$4-$5');
Усложним шаблон функции replace()
, чтобы номер телефона был кликабельным (ссылка с префиксом tel:
):
let pattern = /(\+7|8)[\s(]?(\d{3})[\s)]?(\d{3})[\s-]?(\d{2})[\s-]?(\d{2})/g;
document.body.innerHTML = document.body.innerHTML.replace(pattern, '<a href="tel:+7$2$3$4$5">+7 ($2) $3-$4-$5</a>');
То есть, для корректной работы ссылки нужно в её коде выписать все цифры номера подряд — как раз самый неудобный для чтения человеком вариант.