Профессиональные курсы: Практика верстки HTML JavaScript для начинающих Программирование на PHP Онлайн-курсы по веб-технологиям

Табуляция в текстовых полях форм

Как часто вам приходится писать в текстовых полях на web-страницах текст, требующий табуляцию (например, HTML-код)? Если вы знакомы с этой проблемой, то помните, что рефлекс, выработанный в большинстве редакторов, приводит к потере фокуса у текущего элемента формы. Таково поведение браузера по-умолчанию, которое можно забороть, в случае необходимости, конечно.

Проблема уже решалась, в частности Владимиром Токмаковым. Чем не понравилось предлагаемое решение:

  • Требуется библиотечный файл common.js на 13.5Кб.
  • Сам скрипт обрабатывает все текстовые поля на странице по факту загрузки, что не всегда удобно.
  • Хотелось бы иметь минимальный объем кода для такой простой фичи.

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

function addHandler(object, event, handler, useCapture) {
    if (object.addEventListener) {
        object.addEventListener(event, handler, useCapture ? useCapture : false);
    } else if (object.attachEvent) {
        object.attachEvent('on' + event, handler);
    }
}
function tabInsertInit(obj) {
    var _obj = obj;
    if (window.opera) addHandler(obj, "keypress", function(evt) {tabInsert(evt, _obj);});
    else addHandler(obj, "keydown", function(evt) {tabInsert(evt, _obj);});
}
function tabInsert(evt, obj) {
    evt = evt || window.event;
    var key = evt.keyCode || evt.which;
    if (key == 9 && !evt.ctrlKey && !evt.shiftKey) {
        if (obj.nodeName) if (obj.nodeName.toLowerCase() == "textarea") obj.focus();
        if(document.selection) {
            var iesel = document.selection.createRange().duplicate();
            iesel.text = "\t";
        } else {
            var start = obj.selectionStart;
            var end = obj.selectionEnd;
            var left = obj.value.substring(0, start);
            var right = obj.value.substring(end);
            var scroll = obj.scrollTop;
            obj.value = left + "\t" + right;
            obj.selectionStart = obj.selectionEnd = start + 1;
            obj.scrollTop = scroll;
            obj.focus();
        }
        if(evt.preventDefault) evt.preventDefault();
        evt.returnValue = false;
        return false;
    }
}

Всё что требуется — это передать функции tabInsertInit() ссылку на объект, в котором планируется вставлять табы, вместо потери фокуса. Смотрим пример.

Совместимость

  • WIN: IE7
  • WIN: FF2
  • WIN: Стабильная работа в Opera 9.5. В более низких версиях работает с косяками, характер которых зависит от положения звезд на небе :-)
  • WIN: Safari 3

Обнаружили неработоспособность скрипта в своем браузере? Пишите об этом в комментах, будем бороть.

Александр Бурцев 23 сентября 2008

Все права на статью "Табуляция в текстовых полях форм " принадлежатwebew.ru.

Комментарии

rgbeast 23 сентября 2008, 16:54 #
Столкнулся со следующей проблемой. В firefox нажимаю Ctrl-Tab и попадаю в следующий таб, но при этом в форме остается символ табуляции (неожиданный)
 
Bur 23 сентября 2008, 18:14 #
Спасибо, поправил код и пример. К строке проверки кода клавиши добавилось:
&& !evt.ctrlKey && !evt.shiftKey
 
rgbeast 23 сентября 2008, 23:33 #
спасибо за фикс, работает отлично
 
Riim 21 октября 2009, 13:24 #
Спасибо за статью. Только вопросы появились:
1. в чем смысл конструкции:
Цитата:


var scroll = obj.scrollTop;
obj.scrollTop = scroll;



?

2. зачем obj.focus(); два раза? Зачем он вообще нужен? Разве есть браузер, который снимает фокус с поля при добавлении в него текста?

3. вот в этой статье: http://fastcoder.org/articles/?aid=187
учитывается ситуация когда selectionStart > selectionEnd :
Цитата:


} else if (this.start >= 0 && this.end >= this.start) {


здесь это точно не нужно учитывать?

4. насколько я в курсе "return false;" в старых браузерах не только отменяет действие по умолчанию, но и останавливает всплытие. Останавливать всплытие совсем не нужно, вдруг там, на родительских элементах, еще какие-то обработчики.
 
Bur 21 октября 2009, 13:34 #
1) После присваивания нового значения obj.value у некоторых браузеров скролл в текстарии обнуляется, приходится его руками возвращать на место.
2) Фокус нужен. Давно писал этот скрипт, но фокус там точно не просто так стоит. Можете убрать и поэкспериментировать в каком из браузеров перестанет работать.
3) Не нужно.
4) Это не так. За отмену всплытия отвечает свойство cancelBubble объекта Event.
 
Riim 21 октября 2009, 16:31 #
1) Да, нашелся такой, FF2.0.0.2 .
2) Пока не удается найти. Протестировал в FF(2.0.0.2, 3.0.3, 3.0.4, 3.5.3), Opera(9.23, 9.25, 9.62, 10.alfa1, 10), Crome3.0.195.27, Safari4.0.3, IE(5.5, 6, 7, 8) .
3) Можно поподробней, почему там нужно, а здесь нет?
4)
Цитата:
За отмену всплытия отвечает свойство cancelBubble объекта Event.

или метод stopPropagation, от браузера зависит. А "return false;" это (preventDefault || returnValue) + (stopPropagation || cancelBubble) .
 
Bur 21 октября 2009, 16:44 #
Re 4:
Простые примеры:
1) Мы кроссбраузерно увидим alert, т.к. return false; не отменяет всплывания события.
<div onmousedown="alert('Ups');">
    <div onmousedown="return false;">Click me</div>
</div>

2) Мы кроссбраузерно НЕ увидим алерт, т.к. св-во cancelBubble запрещает всплывание:
<div onmousedown="alert('Ups2');">
    <div onmousedown="(function(e) {e = e || window.event; e.cancelBubble = true;})(event);">Click me2</div>
</div>

Это на примере события onmousedown и других событий мыши. С обработкой события click правила немного другие, НО cancelBubble запрещает всплывание ЛЮБОГО события (в том числе и клика) КРОССБРАУЗЕРНО, в отличие от return false.
 
Riim 21 октября 2009, 17:26 #
Странно тогда что в разных реализациях методов для добавления и удаления событий при "return false;" еще и всплытие останавливают. Не симметрично как-то со стандартным поведением браузеров.
 
Bur 21 октября 2009, 16:45 #
Re 3:
А зачем? Нам здесь не важно положение фокуса в выделении...
 
Riim 21 октября 2009, 17:33 #
Например, есть такая строка '0123456789', selectionStart = 7, selectionEnd = 4

this.value = this.value.slice(0, 7) + '\t' + this.value.slice(4);
this.value = '0123456' + '\t' + '456789';

и результат: '0123456\t45678'
кусок '456' два раза повторяется.
 
Bur 21 октября 2009, 17:36 #
А вы попробуйте на примере.
Выделение заменяется табом, оно не сохраняется.
 
Riim 21 октября 2009, 18:15 #
Я пробовал, работает все верно, но это от того, что даже если выделять справа налево, то selectionStart все равно будет меньше. Увидев эту проверку в статье про вставку кода в текстовые поля, я предположил, что в каких-то браузерах при выделении справа налево в selectionStart будет та позиция, с которой пользователь действительно начал выделять мышью (mousedown, а selectionEnd определяется там, где mouseup). Если мое предположение неверно, то зачем тогда в той статье такая проверка?
 
Bur 21 октября 2009, 18:21 #
Посмотрел, честно говоря не помню.
Кусок объекта я брал из чужого кода (см. самый первый комментарий: "// >>> За основу взят объект, написанный автором Sardar <Sardar@vingrad.ru>"), похоже это условие осталась из того самого кода. Проверю его полезность для скрипта...
 
Bur 21 октября 2009, 16:49 #
Еще, судя адской смеси из preventDefault, returnValue, stopPropagation и cancelBubble вы "плаваете" в реализации в DOM Event2 и реализации событий IE.
 
Riim 21 октября 2009, 17:35 #
Наверно, вам видней, я вообще вот здесь: http://javascript.ru/tutorial/events учился.
 
Bur 21 октября 2009, 17:44 #
Ммм, на javascript.ru всегда хороший материал, но с этим учебником я не знаком.
Лучше читайте реализации от производителей:
http://www.w3.org/TR/DOM-Level-2-Events/events.html
http://msdn.microsoft.com/en-us/library/microsoft.speechserver.dom.mshtml.event.aspx
 
 
Rambler's Top100 Flede HTML valid CSS valid