
Trim на JavaScript: убираем пробелы по краям строки как в VB
В VB лишние пробелы убирает Trim, а в старых браузерах такого метода у строк не было. Пишем три функции с привычными именами и разбираемся, какие символы считать пробелом.
Зачем обрезать пробелы ещё в браузере
Пользователь копирует e-mail из письма, а вместе с адресом в поле попадает пробел или перевод строки. Логин « admin» не совпадает с «admin», проверка на пустое поле пропускает строку из трёх пробелов, а в базе появляются записи, которые потом не находятся поиском. Всё это лечится одной операцией: обрезкой пробельных символов по краям.
В VB для этого есть Trim, LTrim и RTrim, а в JavaScript до появления стандарта ECMAScript 5 у строк не было ни одного подобного метода. Internet Explorer до девятой версии не знал даже String.prototype.trim. Поэтому в legacy-проектах почти всегда встречается самописная функция, и полезно понимать, как она должна работать.
Базовая реализация на регулярных выражениях
Самый короткий путь: заменить пустой строкой последовательность пробелов в начале и в конце. Якорь ^ привязывает шаблон к началу строки, $ к концу, а флаг g нужен, чтобы в одном вызове сработали обе альтернативы.
Функции специально принимают любое значение и приводят его к строке. Так их можно безопасно вызывать для value текстового поля, для числа из JSON и даже для null, который превратится в пустую строку, а не уронит скрипт.
function toStr(v) { return v == null ? '' : String(v); }
// Пробелы слева
function LTrim(s) { return toStr(s).replace(/^\s+/, ''); }
// Пробелы справа
function RTrim(s) { return toStr(s).replace(/\s+$/, ''); }
// С обеих сторон
function Trim(s) { return toStr(s).replace(/^\s+|\s+$/g, ''); }
// Trim(' Visual Basic \t\n') -> 'Visual Basic'Что считать пробелом
VB-функция Trim убирает только обычный пробел с кодом 32. Табуляцию и переводы строк она оставляет. В веб-формах этого мало: из Word и из HTML-страниц часто копируется неразрывный пробел с кодом 160, а в многострочных полях остаются переводы строк.
Класс \s в регулярных выражениях покрывает пробел, табуляцию, перевод строки и возврат каретки. Но в старом Internet Explorer он не включал неразрывный пробел \u00A0, поэтому строка, скопированная со страницы, оставалась «грязной». Надёжнее явно перечислить нужные символы.
- \u0020: обычный пробел;
- \t, \n, \r: табуляция и переводы строк;
- \u00A0: неразрывный пробел, частый гость при копировании из Word;
- \u3000: широкий пробел, встречается в японских и китайских текстах;
- \uFEFF: метка порядка байтов, иногда прилипает к началу содержимого файла.
// Явный набор пробельных символов, одинаковый во всех браузерах
var WS = '[\\s\\u00A0\\u3000\\uFEFF]';
var reTrim = new RegExp('^' + WS + '+|' + WS + '+$', 'g');
function TrimAll(s) {
return (s == null ? '' : String(s)).replace(reTrim, '');
}Быстрый вариант для длинных строк
Регулярное выражение с якорем $ на длинном тексте работает медленно: движок пробует найти совпадение на каждой позиции. Для поля логина это незаметно, а вот обрезка содержимого textarea размером в сотни килобайт в Internet Explorer 6 могла занять заметную долю секунды.
Правую сторону дешевле обрезать циклом: идём с конца, пока встречаем пробельные символы, и один раз вызываем slice. Левую часть оставляем регулярному выражению, там якорь ^ отрабатывает мгновенно.
function TrimFast(s) {
s = (s == null ? '' : String(s)).replace(/^\s+/, '');
var ws = /\s/;
var i = s.length;
while (i > 0 && ws.test(s.charAt(i - 1))) {
i--; // шагаем с конца, пока пробел
}
return s.slice(0, i);
}Расширять String.prototype или нет
Во многих старых проектах функция добавлена прямо в прототип: String.prototype.trim = function () {...}. Это даёт красивый вызов s.trim(), но создаёт конфликт, когда браузер уже умеет trim сам. Ваша версия перезапишет встроенную, а другая библиотека на странице может рассчитывать на стандартное поведение.
Если без прототипа не обойтись, добавляйте метод только при его отсутствии. Это классический полифилл: в современных браузерах работает родная реализация, в старых ваша.
// Добавляем trim, только если браузер его не знает
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\u00A0\uFEFF]+|[\s\u00A0\uFEFF]+$/g, '');
};
}Применяем в форме и не забываем про сервер
Удобно обрезать значения в обработчике отправки формы: пробегаем по текстовым полям, записываем очищенное значение обратно и только потом проверяем обязательные поля. Пароли трогать не надо: пробел в пароле может быть намеренным.
Обрезка в браузере улучшает удобство, но не гарантирует чистоту данных: JavaScript может быть отключён, а запрос можно отправить в обход формы. На стороне ASP.NET или классического ASP значение всё равно нужно пропустить через Trim перед сохранением в базу.
Как это делают сегодня
Вопросы и ответы
Почему Trim не убирает пробел, скопированный из Word?
Скорее всего, это неразрывный пробел с кодом 160. Старые реализации на \s его не видели, поэтому добавьте \u00A0 в набор символов или используйте встроенный trim.
Нужно ли обрезать пробелы внутри строки?
Trim работает только с краями. Для схлопывания повторяющихся пробелов внутри используйте replace(/\s+/g, ' ') после обрезки, но не применяйте это к паролям и форматированному тексту.
Достаточно ли обрезки в браузере?
Нет. Клиентская обрезка нужна для удобства пользователя, а серверная обязательна, потому что запрос может прийти без участия вашего скрипта.