Определение позиции курсора в объекте TEXTAREA

Здравствуйте, уважаемые читатели моего блога. В этой заметке я хотел бы поделиться с вами кросбраузерной, javascript функцией, которая позволяет определить позицию курсора в строке текста объекта TEXTAREA. А также разобраться с тем: «как работает эта функция?»

Начну с того, что данная функция опробована и корректно работает в браузерах: Firefox 3.0.7, Safari 3.1.2, Google Chrome 1.0.154.53, Internet Explorer 7 и Opera 9.6, под Windows Vista. Как оно в других, сказать затрудняюсь, но вроде бы проблем возникнуть не должно. По своей сути, данная функция является «костылями», которые работают на основе определения позиции выделяемой части текста. Возможно, имеется и другое решение, но мне оно неизвестно.

Так или иначе, для работы и понимания функции нам понадобится объект textarea и кнопка, которая будет вызывать функцию с выводом результата в окне alert().

<textarea cols="40" id="textarea"></textarea>
<input onclick="alert('Position: ' + getCaretPos('textarea'))" type="button" value="Position">

Поясню: позицию курсора возвращает функция getCaretPos(), которой мы передаем идентификатор объекта textarea; это значение выводится в окне alert(), при клики по кнопке (событие onclick).

Теперь код самой функции:

function getCaretPos(objName) {
var obj = document.getElementById(objName);
obj.focus();
if (document.selection) { // IE
var sel = document.selection.createRange();
var clone = sel.duplicate();
sel.collapse(true);
clone.moveToElementText(obj);
clone.setEndPoint('EndToEnd', sel);
return clone.text.length;
} else if (obj.selectionStart!==false) return obj.selectionStart; // Gecko
else return 0;
}

Поясню: как уже говорилось выше, функции передается идентификатор объекта textarea, по которому мы получаем переменную obj. Далее мы фокусируемся (focus()) на нашем объекте и приступаем к вычислению позиции курсора.

В случае с Gecko — всё просто. Мы используем свойство selectionStart, которое определяет начальную позицию выделения в объекте. В случае с Internet Explorer пришлось устроить «танцы с бубном»:

  • Cоздаем (TextRange) объект выделенного фрагмента текста (sel), а потом и его дубликат (clone).
  • Используя метод collapse(), производим сжатие объекта TextRange (sel) в точку и помещаем его в начале (true) исходного содержимого объекта.
  • Используя метод moveToElementText(), перемещаем объект TextRange (clone) на фрагмент текста, находящийся в заданном элементе страницы (obj).
  • Используя метод setEndPoint(), устанавливаем границу текущего объекта TextRange (clone) по границе (EndToEnd — конечная с конечной) аналогичного объекта (sel).
  • Остаётся лишь вычислить длину текста полученного объекта TextRange (clone), что и будет текущей позицией курсора.

Проще говоря, по объекту sel вычисляется начальная точка отсчета, а по clone конечная, с дальнейшим «определением» фрагмента текста, длинна которого равна позиции курсора в интересующем нас объекте obj. По крайней мере, так я это себе представляю. Так что не спрашивайте: «почему так, а не иначе, и зачем такой велосипед».

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