Для моего проекта я хотел бы предоставить автоматическое завершение для определенного текстового поля. Подобно тому, как работает intellisense / omnicomplete. Однако для этого мне нужно узнать абсолютную позицию курсора, чтобы я знал, где должен появиться DIV.
Оказывается: этого (почти я надеюсь) достичь невозможно. Есть ли у кого-нибудь отличные идеи, как решить эту проблему?
Вы смотрели решения для редактирования js-кода (я не знаю, есть ли в них автозаполнение)?
приближаемся на хроме: jsbin.com/egadoj/1/edit
@xyu, да, но я боюсь, что здесь их считают тяжеловесными.
Опубликовал потенциальное решение, которое работает намного лучше (комментируя, чтобы уведомить людей, которые смотрят эту ветку)
@SamSaffron: Я имел в виду посмотреть, как они отображают всплывающее окно автозаполнения.
Редакторы на основе @xyu dom (codemirror / ace) могут использовать методы вставки dom для определения местоположения, это довольно просто.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Сообщение в блоге Этот, похоже, отвечает на ваш вопрос, но, к сожалению, автор признает, что тестировал его только в IE 6.
The DOM in IE does not provide information regarding relative position in terms of characters; however, it does provide bounding and offset values for browser-rendered controls. Thus, I used these values to determine the relative bounds of a character. Then, using the JavaScript TextRange, I created a mechanism for working with such measures to calculate the Line and Column position for fixed-width fonts within a given TextArea.
First, the relative bounds for the TextArea must be calculated based upon the size of the fixed-width font used. To do this, the original value of the TextArea must be stored in a local JavaScript variable and clear the value. Then, a TextRange is created to determine the Top and Left bounds of the TextArea.
Я разместил тему, связанную с этой проблемой, на русском JavaScript-сайте.
Если вы не понимаете русский язык, попробуйте перевести его версией Google: http://translate.google.ru/translate?js=y&prev=_t&hl=ru&ie=UTF-8&layout=1&eotf=1&u=http://javascript.ru/forum/events/7771-poluchit-koordinaty-kursora-v- текстовом-полюс-в-пикселях.html & sl = ru & tl = en
Есть некоторые проблемы с разметкой в примерах кода в переведенной версии, поэтому вы можете код читаем в оригинальном посте россии.
Идея проста. Не существует простого, универсального и кросс-браузерного метода для определения положения курсора в пикселях. Честно говоря, есть, но только для Internet Explorer.
В других браузерах, если вам действительно нужно его вычислить, вам нужно ...
Это общий алгоритм, но есть разные нюансы, когда дело касается совместимость с браузером. Команда Component.io собрала простой кроссбраузерный плагин, который работает во всех крайних случаях и не требует jQuery.
Этот блог, похоже, слишком близок к ответу на вопрос. Я сам не пробовал, но автор говорит, что он тестировался с FF3, Chrome, IE, Opera, Safari. Код находится на GitHub
это именно то, что вам нужно, он использует структуру prototype.js и выполняет свою работу. код написан для события щелкнуть, поэтому вы можете добавить к нему событие onkeyup, и готово. Я также тестировал это в IE, Chrome, Firefox, Safari, Opera, и он работает абсолютно. Надеюсь, вы найдете это полезным.
проект на github - хорошее усилие, но далеко не пуленепробиваемое @enobrev имеет гораздо более жесткую реализацию. В частности, код на github не вводит слово-разрыв для переноса слов, он не выполняет замену вставки пробелов должным образом (несколько пробелов отключают его).
Недавно я назначил автора этого плагина осуждать это в пользу textarea-caret-position. @SamSaffron: Я не видел любые репозитории такого рода на GitHub enobrev?
Я не знаю решения для textarea, но оно точно работает для div с contenteditable.
Вы можете использовать Range API. Вот так: (да, вам действительно нужны только эти 3 строки кода)
// get active selection
var selection = window.getSelection();
// get the range (you might want to check selection.rangeCount
// to see if it's popuplated)
var range = selection.getRangeAt(0);
// will give you top, left, width, height
console.info(range.getBoundingClientRect());
Я не уверен в совместимости браузера, но обнаружил, что он работает в последних версиях Chrome, Firefox и даже IE7 (думаю, я тестировал 7, иначе было 9).
Вы даже можете делать «сумасшедшие» вещи вроде этого: если вы набираете "#hash", а курсор находится на последнем h, вы можете посмотреть в текущем диапазоне для символа #, переместить диапазон назад на символы n и получить ограничивающий прямоугольник из в этом диапазоне, это заставит popup-div "придерживаться" слова.
Один небольшой недостаток заключается в том, что contenteditable иногда может немного глючить. Курсор любит перемещаться в невозможные места, и теперь вам нужно иметь дело с вводом HTML. Но я уверен, что производители браузеров решат эти проблемы, если больше сайтов начнут их использовать.
Еще один совет, который я могу дать: посмотрите на библиотеку rangy. Это попытка быть полнофункциональной кросс-совместимой библиотекой диапазонов. Вы не необходимость это, но если вы имеете дело со старыми браузерами, это может того стоить.
Я не буду снова объяснять проблемы, связанные с этим материалом, потому что они хорошо объяснены в других сообщениях. Просто укажу возможное решение, в нем есть ошибка, но это отправная точка.
К счастью, на Github есть скрипт для вычисления позиции курсора относительно контейнера, но для этого требуется jQuery. Страница GitHub здесь: jquery-каретка-позиция-геттер, Спасибо Бевису, Чжао.
На его основе я реализовал следующий код: проверь это в действии здесь, в jsFiddle.net
<html><head>
<meta http-equiv = "content-type" content = "text/html; charset=UTF-8">
<title>- jsFiddle demo by mjerez</title>
<script type = "text/javascript" src = "http://code.jquery.com/jquery-1.8.2.js"></script>
<link rel = "stylesheet" type = "text/css" href = "http://jsfiddle.net/css/normalize.css">
<link rel = "stylesheet" type = "text/css" href = "http://jsfiddle.net/css/result-light.css">
<script type = "text/javascript" src = "https://raw.github.com/beviz/jquery-caret-position-getter/master/jquery.caretposition.js"></script>
<style type = "text/css">
body{position:relative;font:normal 100% Verdana, Geneva, sans-serif;padding:10px;}
.aux{background:#ccc;opacity: 0.5;width:50%;padding:5px;border:solid 1px #aaa;}
.hidden{display:none}
.show{display:block; position:absolute; top:0px; left:0px;}
</style>
<script type = "text/javascript">//<![CDATA[
$(document).keypress(function(e) {
if ($(e.target).is('input, textarea')) {
var key = String.fromCharCode(e.which);
var ctrl = e.ctrlKey;
if (ctrl) {
var display = $("#autocomplete");
var editArea = $('#editArea');
var pos = editArea.getCaretPosition();
var offset = editArea.offset();
// now you can use left, top(they are relative position)
display.css({
left: offset.left + pos.left,
top: offset.top + pos.top,
color : "#449"
})
display.toggleClass("show");
return false;
}
}
});
window.onload = (function() {
$("#editArea").blur(function() {
if ($("#autocomplete").hasClass("show")) $("#autocomplete").toggleClass("show");
})
});
//]]>
</script>
</head>
<body>
<p>Click ctrl+space to while you write to diplay the autocmplete pannel.</p>
</br>
<textarea id = "editArea" rows = "4" cols = "50"></textarea>
</br>
</br>
</br>
<div id = "autocomplete" class = "aux hidden ">
<ol>
<li>Option a</li>
<li>Option b</li>
<li>Option c</li>
<li>Option d</li>
</ol>
</div>
</body>
Скрипт Бевиса - глючит и больше не обслуживается. Я знаю это, потому что оценил все восемь плагинов для получения координат текстового поля на GitHub. На сегодняшний день лучший плагин - textarea-caret-position в компоненте.io. Намного проще, кроссбраузерно и не требует jQuery.
исправил здесь: http://jsfiddle.net/eMwKd/4/
Единственным недостатком является то, что уже предоставленная функция getCaret() разрешает неправильную позицию при нажатии клавиши. поэтому красный курсор кажется позади реального курсора, если вы не отпустите клавишу.
Я еще раз посмотрю на это.
обновление: хм, перенос слов не точен, если строки слишком длинные ..
это самое близкое, что я могу получить: jsbin.com/egadoj/1/edit волосы от решенного
приближается jsbin.com/egadoj/4
это довольно хорошо, вам нужно только проверить, находитесь ли вы в конце строки (в этом случае предварительный просмотр не работает)
Кстати, я также проверил функцию getCaret(), и, похоже, невозможно заставить ее работать при нажатии клавиши. Случаи, которые невозможно исправить, - это когда вы просматриваете вверх или вниз с помощью курсора.
Это действительно непростая проблема, но команда Component.io собрала плагин textarea-caret-position, который в значительной степени идеален (без зависимостей, кроссбраузерность, только 80 строк кода, обрабатывает полосы прокрутки, перенос, любую комбинацию шрифтов и т. д.)
возможно, это вас порадует, он покажет позицию выделения и положение курсора, поэтому попробуйте проверить таймер, чтобы получить автоматическое положение, или снимите флажок, чтобы получить положение, нажав кнопку Получить выбор
<form>
<p>
<input type = "button" onclick = "evalOnce();" value = "Get Selection">
timer:
<input id = "eval_switch" type = "checkbox" onclick = "evalSwitchClicked(this)">
<input id = "eval_time" type = "text" value = "200" size = "6">
ms
</p>
<textarea id = "code" cols = "50" rows = "20">01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 Sample text area. Please select above text. </textarea>
<textarea id = "out" cols = "50" rows = "20"></textarea>
</form>
<div id = "test"></div>
<script>
function Selection(textareaElement) {
this.element = textareaElement;
}
Selection.prototype.create = function() {
if (document.selection != null && this.element.selectionStart == null) {
return this._ieGetSelection();
} else {
return this._mozillaGetSelection();
}
}
Selection.prototype._mozillaGetSelection = function() {
return {
start: this.element.selectionStart,
end: this.element.selectionEnd
};
}
Selection.prototype._ieGetSelection = function() {
this.element.focus();
var range = document.selection.createRange();
var bookmark = range.getBookmark();
var contents = this.element.value;
var originalContents = contents;
var marker = this._createSelectionMarker();
while(contents.indexOf(marker) != -1) {
marker = this._createSelectionMarker();
}
var parent = range.parentElement();
if (parent == null || parent.type != "textarea") {
return { start: 0, end: 0 };
}
range.text = marker + range.text + marker;
contents = this.element.value;
var result = {};
result.start = contents.indexOf(marker);
contents = contents.replace(marker, "");
result.end = contents.indexOf(marker);
this.element.value = originalContents;
range.moveToBookmark(bookmark);
range.select();
return result;
}
Selection.prototype._createSelectionMarker = function() {
return "##SELECTION_MARKER_" + Math.random() + "##";
}
var timer;
var buffer = "";
function evalSwitchClicked(e) {
if (e.checked) {
evalStart();
} else {
evalStop();
}
}
function evalStart() {
var o = document.getElementById("eval_time");
timer = setTimeout(timerHandler, o.value);
}
function evalStop() {
clearTimeout(timer);
}
function timerHandler() {
clearTimeout(timer);
var sw = document.getElementById("eval_switch");
if (sw.checked) {
evalOnce();
evalStart();
}
}
function evalOnce() {
try {
var selection = new Selection(document.getElementById("code"));
var s = selection.create();
var result = s.start + ":" + s.end;
buffer += result;
flush();
} catch (ex) {
buffer = ex;
flush();
}
}
function getCode() {
// var s.create()
// return document.getElementById("code").value;
}
function clear() {
var out = document.getElementById("out");
out.value = "";
}
function print(str) {
buffer += str + "\n";
}
function flush() {
var out = document.getElementById("out");
out.value = buffer;
buffer = "";
}
</script>
посмотрите демо здесь: jsbin.com
это просто местоположение курсора, довольно просто решить кроссбраузерность и очень хорошо документировано
Вот описание одной хитрости для смещения каретки: Координаты каретки Textarea X / Y - плагин jQuery
Также будет лучше использовать элемент div с атрибутом contenteditable, если вы можете использовать функции html5.
Как насчет добавления элемента span к клонируемому div и установки фальшивого курсора на основе смещений этого диапазона? Я обновил вашу скрипку здесь. Также здесь только бит JS
// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
var map = [];
var pan = '<span>|</span>'
//found @ http://davidwalsh.name/detect-scrollbar-width
function getScrollbarWidth() {
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
// Delete the DIV
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
function getCaret(el) {
if (el.selectionStart) {
return el.selectionStart;
} else if (document.selection) {
el.focus();
var r = document.selection.createRange();
if (r == null) {
return 0;
}
var re = el.createTextRange(),
rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}
$(function() {
var span = $('#pos span');
var textarea = $('textarea');
var note = $('#note');
css = getComputedStyle(document.getElementById('textarea'));
try {
for (i in css) note.css(css[i]) && (css[i] != 'width' && css[i] != 'height') && note.css(css[i], css.getPropertyValue(css[i]));
} catch (e) {}
note.css('max-width', '300px');
document.getElementById('note').style.visibility = 'hidden';
var height = note.height();
var fakeCursor, hidePrompt;
textarea.on('keyup click', function(e) {
if (document.getElementById('textarea').scrollHeight > 100) {
note.css('max-width', 300 - getScrollbarWidth());
}
var pos = getCaret(textarea[0]);
note.text(textarea.val().substring(0, pos));
$(pan).appendTo(note);
span.text(pos);
if (hidePrompt) {
hidePrompt.remove();
}
if (fakeCursor) {
fakeCursor.remove();
}
fakeCursor = $("<div style='width:5px;height:30px;background-color: #777;position: absolute;z-index:10000'> </div>");
fakeCursor.css('opacity', 0.5);
fakeCursor.css('left', $('#note span').offset().left + 'px');
fakeCursor.css('top', textarea.offset().top + note.height() - (30 + textarea.scrollTop()) + 'px');
hidePrompt = fakeCursor.clone();
hidePrompt.css({
'width': '2px',
'background-color': 'white',
'z-index': '1000',
'opacity': '1'
});
hidePrompt.appendTo(textarea.parent());
fakeCursor.appendTo(textarea.parent());
return true;
});
});
Взаимодействие с другими людьми ОБНОВИТЬ: Я вижу, что есть ошибка, если первая строка не содержит жестких разрывов строк, но если это так, похоже, работает хорошо.
Мне понравилась информация о нахождении ширины полос прокрутки. Есть обычный ошибка, однако, что большинство библиотек координат каретки имеют. component.io плагин textarea-caret-position решает эту проблему, к тому же не требует jQuery, работает в Chrome, FF и IE и занимает всего 80 строк кода.
Обратите внимание, что этот вопрос дублирует вопрос, заданный месяцем ранее, и я ответил на него здесь. Я сохраню ответ только по этой ссылке, так как этот вопрос должен был быть закрыт как дубликат много лет назад.
Я искал плагин координат текстового поля для метеор-автозаполнение, поэтому я оценил все 8 плагинов на GitHub. Победителем, безусловно, является текстовое поле-каретка-позиция из Компонент.

Зеркало <div> создано за кадром и оформлено в точности как <textarea>. Затем текст текстового поля до курсора копируется в div, и сразу после него вставляется <span>. Затем текстовое содержимое диапазона устанавливается равным оставшейся части текста в текстовой области, чтобы точно воспроизвести обертку в поддельном div.
Это единственный метод, который гарантированно обрабатывает все крайние случаи, связанные с переносом длинных строк. Он также используется GitHub для определения позиции раскрывающегося меню пользователя @.
скопируйте-вставьте эту строку «iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiiiiiiiiiiiiiiiiii» и наслаждайтесь результатом. Он полностью не работает в Firefox, а также есть проблема в Chrome.
Было бы лучше подумать о том, чтобы отказаться от textarea и вместо этого использовать contenteditable div? Просто выбросил это там.