Trim на JavaScript: убираем пробелы по краям строки как в VB

РазделJavaScript и CSSУровеньНовичок Чтение7 минОпубликовано ТегиJavaScriptСтрокиФормы

В VB лишние пробелы убирает Trim, а в старых браузерах такого метода у строк не было. Пишем три функции с привычными именами и разбираемся, какие символы считать пробелом.

Главное. Trim в JavaScript пишется одним регулярным выражением, но набор пробельных символов лучше задать явно, включая неразрывный пробел. Встроенный метод не перезаписывайте, а серверную очистку не отменяйте.

Зачем обрезать пробелы ещё в браузере

Пользователь копирует e-mail из письма, а вместе с адресом в поле попадает пробел или перевод строки. Логин « admin» не совпадает с «admin», проверка на пустое поле пропускает строку из трёх пробелов, а в базе появляются записи, которые потом не находятся поиском. Всё это лечится одной операцией: обрезкой пробельных символов по краям.

В VB для этого есть Trim, LTrim и RTrim, а в JavaScript до появления стандарта ECMAScript 5 у строк не было ни одного подобного метода. Internet Explorer до девятой версии не знал даже String.prototype.trim. Поэтому в legacy-проектах почти всегда встречается самописная функция, и полезно понимать, как она должна работать.

Базовая реализация на регулярных выражениях

Самый короткий путь: заменить пустой строкой последовательность пробелов в начале и в конце. Якорь ^ привязывает шаблон к началу строки, $ к концу, а флаг g нужен, чтобы в одном вызове сработали обе альтернативы.

Функции специально принимают любое значение и приводят его к строке. Так их можно безопасно вызывать для value текстового поля, для числа из JSON и даже для null, который превратится в пустую строку, а не уронит скрипт.

trim.jsJavaScript
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: метка порядка байтов, иногда прилипает к началу содержимого файла.
trim.jsJavaScript
// Явный набор пробельных символов, одинаковый во всех браузерах
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. Левую часть оставляем регулярному выражению, там якорь ^ отрабатывает мгновенно.

trim-fast.jsJavaScript
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 сам. Ваша версия перезапишет встроенную, а другая библиотека на странице может рассчитывать на стандартное поведение.

Если без прототипа не обойтись, добавляйте метод только при его отсутствии. Это классический полифилл: в современных браузерах работает родная реализация, в старых ваша.

polyfill.jsJavaScript
// Добавляем 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, а также trimStart и trimEnd, и регулярное выражение для обрезки больше не нужно. Встроенный trim учитывает и неразрывный пробел, и BOM. В .NET аналогом служит String.Trim без аргументов, который убирает все символы Unicode с признаком пробела, а валидацию форм удобно дополнять атрибутами required и pattern.

Вопросы и ответы

Почему Trim не убирает пробел, скопированный из Word?

Скорее всего, это неразрывный пробел с кодом 160. Старые реализации на \s его не видели, поэтому добавьте \u00A0 в набор символов или используйте встроенный trim.

Нужно ли обрезать пробелы внутри строки?

Trim работает только с краями. Для схлопывания повторяющихся пробелов внутри используйте replace(/\s+/g, ' ') после обрезки, но не применяйте это к паролям и форматированному тексту.

Достаточно ли обрезки в браузере?

Нет. Клиентская обрезка нужна для удобства пользователя, а серверная обязательна, потому что запрос может прийти без участия вашего скрипта.

Читайте также