Textarea, Enter и горячие клавиши

Published: 01.09.2011

Что произойдёт с html-формой, при условии, что фокус находится на одном из её элементов, если нажать клавишу Enter? Правильно, произойдёт отправка формы. Кроме одного случая: когда фокус находится у <textarea>. В этом случае, внезапно! предсказуемо будет вставлен символ конца строки. В общем, отправить форму «энтером» с ходу не получится - надо или тянуться мышкой до кнопки «Отправить», или табуляцией переключиться на другой элемент формы (главное, чтобы не на следующий <textarea>). Вот так, например: Пример 0.

Но! Как говорится, если нельзя, но очень хочется, то — можно.

— И как же это сделать? — Для этого необходимо переопределить поведение браузера при нажатии клавиши Enter. — Но, <textarea> — это, всё-таки, многострочное поле, хотелось бы сохранить возможность перехода на новую строку. — Не проблема, можем назначить для этой операции сочетание клавиш Ctrl+Enter. — О, да, это то, чего мне не хватало в жизни, из-за чего меня бросила жена и презирали дети! Давай же сделаем это!

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

Но мы лёгких путей не ищем, поэтому велосипед построим. Он будет очень маленьким, и в этом его преимущество перед готовыми решениями — никакой универсальности и лишнего кода, только то, что надо для выполнения поставленной задачи. Итак:

Пример 1: малэнкий, но очин гордый вэлосипед
  1. ctrl = false; // признак нажатой клавиши "Ctrl"
  2.  
  3. $('#textarea').keydown(function(event){
  4.   switch (event.which) {
  5.     case 13: return false; // отключаем стандартное поведение
  6.     case 17: ctrl = true; // клавиша Ctrl нажата и удерживается
  7.   }
  8. });
  9. $('#textarea').keyup(function(event){
  10.   switch (event.which) {
  11.     case 13:
  12.       if (!ctrl) { // если ctrl не нажат
  13.         $('#form').submit(); // отправляем форму
  14.         return false;
  15.       }
  16.       breakText(); // иначе вставляем конец строки
  17.     break;
  18.     case 17: ctrl = false; // Ctrl отпустили
  19.   }          
  20. });

Протестируем то, что получилось: Пример 1. Обратите внимание на функцию breakText() на строке 16, которая непосредственно вставляет символ конца строки в то место, где находится курсор, но её реализация будет описана после того, как разберёмся с горячими клавишами.

Пример 2: Query.keyboard

Ну что же, рассмотрим универсальные hotkeys-библиотеки. Первой на очереди jQuery.keyboard от Павла Пономаренко (aka TheShock):

  1. $('#textarea').keyboard('enter', function(e, bind) {
  2.   $('#form').submit();
  3.   return false;
  4. });
  5. $('#textarea').keyboard('ctrl+enter', function() {
  6.   breakText_rangyinputs();
  7. });

Смотрим: Пример 2. Работает, но, всё же есть одна проблема. Её упоминает и автор библиотеки: «Возможно, кое-где некорректно работает preventDefault (зависит от браузера?)». Который по умолчанию должен отключаться. Т.е., помимо добавленного нами сабмита формы, происходит выполнение стандартного поведения браузера - в нашем случае перед перезагрузкой формы можно увидеть как курсор прыгает на следующую строку. К сожалению «кое-где» — это везде, кроме Oперы. В даном примере на это можно закрыть глаза, страничка всё равно перезагружается, но, если бы данные отправлялись через AJAX, например, всё было бы намного хуже. В первом примере и, забегая вперёд, в последующем этой проблемы нет. Опять же, функция breakText_rangyinputs() делает то же, что и breakText(), но об этом после.

Пример 3: jquery.hotkeys

Страничка проекта.

  1. $('#textarea').bind('keydown', 'return', function() {
  2.   $('#form').submit();
  3.   return false;
  4. });
  5. $('#textarea').bind('keydown', 'Ctrl+return', function() {
  6.   breakText_rangyinputs();
  7. });

Тестируем: Пример 3. Работает.

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

Вставка текста в позицию курсора

Но это не всё, вернёмся к нашим баранам. В смысле, к функции вставки переноса строки. Задача тоже очень проста: определить позицию курсора, вставить туда перенос строки и установить курсор в начало новой строки.

По традиции, первый пример — рукописный:

  1. function breakText() {
  2.   // определяем позицию курсора
  3.   var caret = getCaretPosition('textarea');
  4.   var text = $('#textarea').val();
  5.   // вставляем перенос строки
  6.   $('#textarea').val(text.substring(0, caret)+'\r\n'+(text.substring(caret)));
  7.   // Передвигаем курсор на новую строку.
  8.   // Обратите внимание на fix в передаче параметра позиции курсора
  9.   // функции setCaretPosition() для браузера Opera.
  10.   setCaretPosition('textarea', ($.browser.opera) ? caret+2 : caret+1);
  11. }

Не буду приводить текст функций setCaretPosition и getCaretPosition, которые были взяты отсюда и отсюда, их легко можно найти, просмотрев исходный код моих примеров.

Опять же, можно подключить готовую библиотеку для работы с текстом, это утяжелит ваше приложение на насколько килобайт, но прибавит изящества коду. Из ряда подобных jQuery-плагинов я взял поновее: Rangyinputs

  1. function breakText_rangyinputs() {
  2.   var caret = $('#textarea').getSelection().start;
  3.   $('#textarea').insertText('\r\n', caret, false).setSelection(caret+1, caret+1);
  4. }

И с Оперой проблем нет. Удачи :)

Скачать архив с исходным кодом