heap.tech
лаборатория велосипедов
×

JavaScript newline (CRLF) length

22 мая 2016
Недавно решал задачу с нотами и поэтессами веб-формой и полем для ввода (textarea). Нужно ограничивать количество введенных символов в текстовое поле в зависимости от условий. Например один пользователь может написать 1000 символов, другой только 100, а третий вообще без ограничений. Плюс должно быть окошечко, которое отображает кол-во уже введенных и кол-во символов, которое можно еще ввести.
Все как обычно, лейбл, текстареа, форма, кнопка, овер9000 раз такое делал, пара минут и готово. Проверяю. Ввел 100 символов (допустимый предел кол-ва символов), нажимаю submit. Ну вроде бы все ок, работает. Данные пришли на сервер, опять проверяю кол-во символов и БАЦ! Длинна полученной строки равна 116 символам!
- Кодировки, фронтенд 8-битный utf, бекенд 32 ? Не может быть, какая разница сколько бит на символ, считаю именно символы.
- В текстовое поле дописывается что-то при пост-запросе ? Опять нет

Далее уменьшил кол-во символов до 10, то есть ввел что-то типа 1234567890, submit. На сервере получаю правильную длину строки, ВАТ ??
Методом научного перебора я понял, что расхождение между JS и бекендом возникает, когда появляется перенос строки. Гугл подтвердил.
В JS перенос строки это \n, то есть один символ, а CLR и MSSQL представляю перенос строки как два символа \r\n.
Кстати в PHP перенос строки это тоже один символ, как обстоят дела с MySQL не знаю, но думаю также.

Но это еще не все, есть еще одна фича. Движок webkit и javascript (прим. Сhrome, js v8) работают не одинаково. И, если ввести строку (без кавычек)

'abcd efg'


в текстовое поле, то его значение будет иметь разную длину. JS покажет 9, а webkit 10 символов.
Чтобы убедиться в этом - нужно задать свойство maxlength текстовому полю. Движок браузера позволит вместить всю строку целиком, тогда как HTMLTextAreaElement.value.length покажет 9 символов - то есть, по логике, еще можно ввести два символа. Но это не так.

Ладно, мы поняли, что каждый перенос строки это два символа, написать функцию правильного подсчета длины строки для textarea элементарно
function l() { var t = this, v = t.value, l = v.length; var r = v.match(/\n/g); return r && r.length ? l + r.length : l; }

Просто добавляет кол-во переносов к длине строки.
Следующий шаг это обрезать строку до определенного символа. Нативная String.Substr не годиться, так как подсчет порядкового номера символа (второй аргумент) происходит согласно тому, что перенос строки это один символ. Велосипед - настало твое время!

Значит нужно разбивать всю строку по строкам. Далее перебрать все строки складывая длины каждой и еще плюс два. Так дойти до строки, длина которой выходит за пределы крайнего символа, значение которого передано в функцию (папр. переменная limit). Обрезать последнюю строку до разницы между переданным значением limit и общей длиной всей строки. Звучит сложновато, но на деле все гораздо проще

function Substr(limit) { var t = this, v = t.value; if (this.Length() > limit) { if (this.Length() == v.length) { this.value = v.substr(0, limit); } else if (this.Length() > v.length) { var lines = v.split(/.*\r\n|\r|\n/g); var totalLength = 0; var cuttedValue = []; for (var l in lines) { if (totalLength + lines[l].length + 2 <= limit) { totalLength += (lines[l].length + 2); cuttedValue.push(lines[l] + '\r\n'); } else { cuttedValue.push(lines[l].substr(0, limit - totalLength)); break; } } this.value = cuttedValue.join(''); } } }


Готово.
Функция ориентирована на контекст вызова HTMLTextAreaElement.Substr. Поэтому нужно расширить прототип textarea джвумя двумя функциями.

Код полностью
(function () { document.body.onload = function () { function implementCRLF() { var e = document.getElementsByTagName('textarea'); var fn = function () { return { Length: function () { var t = this, v = t.value, l = v.length; var r = v.match(/\n/g); return r && r.length ? l + r.length : l; }, Substr: function (limit) { var t = this, v = t.value; if (this.Length() > limit) { if (this.Length() == v.length) { this.value = v.substr(0, limit); } else if (this.Length() > v.length) { var lines = v.split(/.*\r\n|\r|\n/g); var totalLength = 0; var cuttedValue = []; for (var l in lines) { if (totalLength + lines[l].length + 2 <= limit) { totalLength += (lines[l].length + 2); cuttedValue.push(lines[l] + '\r\n'); } else { cuttedValue.push(lines[l].substr(0, limit - totalLength)); break; } } this.value = cuttedValue.join(''); } } } }; }(); for (var i = 0; i < e.length; i++) { e[i].Length = fn.Length; e[i].Substr = fn.Substr; } } implementCRLF(); } })();


Код на github
 
1313
0

Оставлять комментарии могут только зарегистрированные пользователи

пока никто не оставлял комментариев