Однажды, для одного из проектов мне потребовалось автоматическое увеличение textarea c некоторым содержанием, которое будет загруженной в динамически через Ajax. Мне не была известна высота содержимого, а элемент textarea, в отличие от других HTML элементов, не изменяет свою высоту автоматически при добавлении контента, поэтому мне пришлось обновлять высоту textarea с помощью JavaScript каждый раз, когда его содержание изменилось.
Казалось бы, простая задача и, наверняка, кто-то когда-нибудь нечто подобное уже делал. К сожалению, те, что я нашел, оказались слишком сложными, включали заумные математические вычисления и не корректно работали, если блок с неким текстом изначально был скрыт. Давайте изучим лучший способ, который будет прост и решит проблему с первоначально скрытым полем для ввода текста.
Использование клонированного скрытого элемента
Элемент div, как вам известно, будет растягиваться, чтобы соответствовать высоте его содержания (при условии отсутствия обтекания или абсолютного позиционирования). Таким образом, чтобы получить высоту текстового поля, просто нужно сделать следующее:
- Взять содержимое, загруженное в textarea
- Создать невидимый клон DIV
- Назначить клону одинаковую ширину и типографские свойства, как у textarea
- Поместить содержимое в клон
- Получить высоту клона
- Сделайть у textarea такую же высоту как и у клона
Код
Одним из ключей к этому решению является CSS. Как уже упоминалось, невидимый клон должен иметь те же свойства, что и textarea. Это не только font-size и font-family, но и white-space и word-wrap.
Вот CSS для textarea:
textarea {
width: 500px;
min-height: 50px;
font-family: Arial, sans-serif;
font-size: 13px;
color: #444;
padding: 5px;
}
.noscroll {
overflow: hidden;
}
Обратите внимание, что я добавил отдельный класс с overflow: hidden
, чтобы предотвратить появление прокрутки. Добавлять такое свойству элементу - не лучшее решение, так часть содержимого может быть не видна, но, поскольку я буду его добавлять изменяя высоту с помощью JavaScript, то все будет нормально. Этот класс будет добавлен в textarea JavaScript'ом, чтобы гарантировать, что если JavaScript выключен у пользователя в браузере, содержимое textarea можно будет поскроллить и мы все увидим.
Вот CSS, которые я буду применять к скрытому клону textarea:
.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 и продублировал несколько типографских свойств. Также я даю клону и текстовой зоне min-height
, так что она всегда стартует на стандартной используемой высоте.
Теперь, набросаем по-быстрому код на jQuery:
$(function() {
var txt = $('#comments'),
hiddenDiv = $(document.createElement('div')),
content = null;
txt.addClass('txtstuff');
hiddenDiv.addClass('hiddendiv common');
$('body').append(hiddenDiv);
txt.on('keyup', function () {
content = $(this).val();
content = content.replace(/\n/g, '<br>');
hiddenDiv.html(content + '<br class="lbr">');
$(this).css('height', hiddenDiv.height());
});
});
Этот код предполагает, что у нас один textarea на странице. Если вам нужно обработать несколько textarea, то нам придется поправить первую строку внутри функции.
Я динамически изменяю высоту на основе JQuery-события в keyup
. Вы можете легко изменить это, чтобы ответить на запрос Ajax, если загружаете содержимое этим способом.
Использование keyup
, тем не менее, хорошее решение, потому что наиболее вероятная причина, по которой нужно автоматически изменять высоту textarea - ввод пользователем данных.
А что насчет IE8?
Увы и ах, наш распрекрасный скрипт отказался корректно работать в IE8. Если вы до сих пор пользуетесь им, то добавьте следующие строки в наш код:
// фикс проблемы с innerHTML в IE
content = content.replace(/\n/g, '<br>');
Но даже после добавления этой строки, у нас по-прежнему проблема: длинная, непрерывная строки текста не повлияют на высоту текстового поля. Это, на самом деле, не является большой проблемой: просто добавьте для клона textarea CSS-свойство word-wrap: break-word
.
Превращаем в jQuery-плагин
Наш скрипт с автоматическим растягиванием textarea может быть легко превращен в плагин:
(function($){
$.fn.textareaAutoresize = function(options) {
//код, отвечающий за автоматическое
//увеличение textarea
}
})(jQuery);
После чего, нам остается вызвать его $(".autoresize").textareaAutoresize();
и наслаждаться. Скрипт превосходно работает, пока мы вводим в textarea текст. Если же мы вставим теги, да еще со стилями... догадываетесь, что произойдет? Правильно! Код внутри textarea подставится в наш клон, а поскольку он представляет собой блочный элемент, то его высота рассчитается с учетом стилей подставленных тегов. Давайте внесем изменение в наш скрипт, добавив замену символов <
и >
на, допустим, *
:
var content = $(this).val()
.replace(/[<,>]/g, '*')
.replace(/\n/g, '<br>');
Послесловие: оптимизация кода
Как вы должно быть заметили, код наш не совсем идеален в плане быстродействия. Виной всему... да-да, строка var content = $(this).val().replace(/[<,>]/g, '*').replace(/\n/g, '<br>');
. Прежде чем делать перебор строки посимвольно в поиске переноса на новую строку (replace(/\n/g, '<br>')
) было бы неплохо определить версию браузера и запускакть перебор только если это IE8 и ниже. Например, добавляем в объект settings свойство ie8:
var settings = $.extend({
hiddenDiv:null,
ie8:jQuery.support.opacity //IE8 и ниже не поддерживают css свойство opacity
}, options);
После этого пишем if(!settings.ie8) content.replace(/\n/g, '<br>');
и отсекаем ресурсоемкую оперецию в современных браузерах.
Тем не менее, наш скрипт и так работает достаточно шустро, поэтому я не стал заморачиваться. Но, если у вас возникнут проблемы с быстродействием, вы знаете, что можно сделать.