Автоматическое изменение высоты у элемента textarea
Для одного проекта передо мной стояла задача найти способ автоматически изменять высоту элемента textarea
согласно тому контенту, который будет динамически загружаться посредством Ajax. Так как высота контента мне была неизвестна, а элемент textarea
не может подстраивать свои размеры под контент так же гармонично, как это делают другие HTML элементы, то при каждом изменении контента мне приходилось вручную вносить высоту элемента в JavaScript.
<Textarea>
- многострочное поле текстового ввода в форме. Вроде ничего сложного. Найденные мною в интернете плагины и скрипты, решающие эту проблему, показались мне излишне запутанными, к тому же большинство решений включали в себя мудреные математические вычисления. Приведу более простое решение.
Воспользуемся невидимым элементом-клоном
Элемент <div>
будет растягиваться, чтобы подстроиться под высоту своего контента (при условии отсутствия абсолютно позиционированных элементов и плавающих объектов). Итак, чтобы получить высоту textarea
, нужно сделать следующее:
- Захватить контент, загруженный в элемент
textarea
- Создать невидимый элемент-клон (
div
) - Задать для элемента-клона такие же типографические свойства и ширину, как и у элемента
textarea
. - Поместить контент в клон
- Получить высоту элемента-клона
- У элемента textarea высоту сделать равной высоте элемента-клона
Код
Ключевую роль в моем решении будет играть CSS. Как упоминалось выше, невидимый клон должен иметь те же типографические свойства, что и элемент textarea
. Поэтому свойства font-siz
e, font-family
, white-space
и word-wrap
элемента-клона должны соответствовать свойствам font-size
, font-family
, white-space
и word-wrap
элемента textarea.
CSS
textarea {
width: 500px;
min-height: 50px;
font-family: Arial, sans-serif;
font-size: 13px;
color: #444;
padding: 5px;
}
.noscroll {
overflow: hidden;
}
Заметьте, что мною был добавлен отдельный класс с объявлением overflow: hidden
. Это было сделано для того, чтобы предотвратить появление scrollbar-ов. Обычно добавлять такое объявление тегу textarea
весьма сомнительно. Но в нашем случае оно уместно, так как я буду менять размеры элемента textarea
с помощью JavaScript. Этот класс будет добавлен к textarea
посредством JavaScript, что обеспечит нам скроллирование поля формы в случае, если JavaScript выключен.
СSS невидимого элемента-клона
CSS
.hiddendiv {
display: none;
white-space: pre-wrap;
width: 500px;
min-height: 50px;
font-family: Arial, sans-serif;
font-size: 13px;
padding: 5px;
word-wrap: break-word;
}
Краткий анализ: во-первых, я использую объявление display: none
, так как я не хочу, чтобы блок-клон видел пользователь. Полагаю, что это сгодится и для скрин-ридеров, так как, опять же, я не хочу, чтобы они могли прочитать информацию с элемента-клона. Если у кого-то есть лучшее решение, чтобы спрятать блок от вспомогательных устройств, пожалуйста, дайте мне знать.
Свойству white-space
задано значение pre-wrap
, что означает: в тексте сохраняются все пробелы и переносы, однако если строка по ширине не помещается в заданную область, то текст автоматически будет перенесен на следующую строку. Элементу-клону я задал такую же ширину, как и у элемента textarea
, и продублировал типографические свойства. И клон, и textarea
имеют одинаковый min-height
, чтобы они всегда изначально имели стандартный и практичный вид.
Перейдем к jQuery
JQuery
$(function() {
// описываем поле ввода (тег textarea)
var txt = $('#comments'),
// создаем элемент-клон
hiddenDiv = $(document.createElement('div')),
content = null;
// добавляем класс .noscroll элементу #comments(эл-т textarea)
txt.addClass('noscroll');
// добавляем класс .hiddendiv элементу-клону
hiddenDiv.addClass('hiddendiv');
//добавляем элемент-клон в тег body
$('body').append(hiddenDiv);
//связываем событие keyup (пользователь отпускает
//клавишу клавиатуры)
//c функцией. Все приминительно к элементу textarea#comments
txt.bind('keyup', function() {
// возвращаем значение атрибута value элемента
// #comments(textarea)
// (вводимые пользователем данные)
content = txt.val();
// символ перевода каретки (\n), везде (g) меняем на <br>
content = content.replace(/\n/g, '<br>');
hiddenDiv.html(content);
// назначаем высоту элемену #comments(эл-т textarea)
txt.css('height', hiddenDiv.height());
});
});
Этот код показывает, что нашей целью является единственный элемент textarea на странице. В случае, когда этих элементов несколько, нужно изменить первую строку в функции, где идет описание элемента, с которым мы работаем.
Как видите, высота будет меняться автоматически посредством событие keyup
. Очень удобно, если для загрузки контента вы используете Ajax.
Как насчет IE6-8?
Я чуть не бросил написание этой статьи, так как код не работал в IE6-8. Не работал он в связи со слабой обработкой в IE внутреннего содержания HTML. Решение я все же нашел, плюс мне попался jQuery плагин. Он использует тот же метод, что и я (клонированный элемент), и работал он (главным образом) в IE.
Вот строка, которую я позаимствовал из этого примера и которая решает (главным образом) мою проблему с IE:
CSS
content = content.replace(/\n/g, '<br>');
Однако даже после добавления этой строки все еще оставалась одна загвоздка: длинные строки текста не влияли на высоту элемента textarea
. Решить эту проблему помогло добавление к элементу-клону объявление word-wrap: break-word
к CSS.
Взглянуть на демо вы можете, кликнув по ссылке ниже. Дайте мне знать, если столкнетесь с какими-либо проблемами.
Использованы материалы статьи
Комментарии к статье
А демка не работает (Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2)
может быть, только что проверил в своих старых (IE 7, FF 4.0.1, opera 10) - все работает, Да и все статьи - это всего лишь идеи, никогда не буду проверять на 100% кроссбраузерность.
ни в одном браузере не работает....
работает, включить javascript стоит
Зачем извраты с div-ами?
function textAreaHeight(textarea) {
if (!textarea._tester) {
var ta = textarea.cloneNode();
ta.style.position = 'absolute';
ta.style.zIndex = -2000000;
ta.style.visibility = 'hidden';
ta.style.height = '1px';
ta.id = '';
ta.name = '';
textarea.parentNode.appendChild(ta);
textarea._tester = ta;
textarea._offset = ta.clientHeight - 1;
}
if (textarea._timer) clearTimeout(textarea._timer);
textarea._timer = setTimeout(function () {
textarea._tester.style.width = textarea.clientWidth + 'px';
textarea._tester.value = textarea.value;
textarea.style.height = (textarea._tester.scrollHeight - textarea._offset) + 'px';
textarea._timer = false;
}, 1);
}
Это надо повесить на onkeydown текстбокса.
Отличная штука, работает лучше даже, чем в других местах предлагали. Спасибо!