Ответ 1
Здесь используется подход rangyinputs, rangy и jQuery.
Он в основном копирует весь текст из textarea
в div
того же размера. Я установил некоторый CSS, чтобы гарантировать, что в каждом браузере textarea
и div
обернут их содержимое точно так же.
Когда нажата кнопка textarea
, я читаю, в каком символе указывается каретка, затем вставляем каретку span
с тем же индексом внутри div
. Только делая это, у меня возникла проблема с каретой span
, возвращающейся к предыдущей строке, если пользователь нажал в начале строки. Чтобы исправить это, я проверяю, является ли предыдущий символ space
(что позволит сделать обертку), если это true
, я завершаю его в span
, и я завершаю следующее слово (прямое после позиции каретки) в span
. Теперь я сравниваю верхние значения между этими двумя span
's, если они отличаются друг от друга, происходит некоторая обертка, поэтому я предполагаю, что значения top
и left
#nextword
span
эквивалентны в положение каретки.
Этот подход все еще может быть улучшен, я уверен, что я не думал обо всем, что могло бы пойти не так, и даже если бы у меня было, то я не потрудился внедрить исправление для всех, поскольку я У меня есть время, чтобы сделать это в данный момент, несколько вещей, которые вам нужно будет посмотреть:
-
он еще не обрабатывает жесткие возвраты, вставленные с помощью Enter(исправлено) -
позиционирование прерывается при вводе нескольких пробелов в строке(исправлено) - Я думаю, что дефисы позволят обертывать содержимое, а также.
В настоящее время он работает точно так же в браузерах здесь, в Windows 8, с последними версиями Chrome, Firefox, IE и Safari. Однако мои тесты не были очень строгими.
Здесь jsFiddle.
Я надеюсь, что это поможет вам, по крайней мере, это может дать вам некоторые идеи, чтобы опираться.
Некоторые функции:
-
Я включил
ul
для вас, который позиционируется в нужном месте, и исправил проблему с Firefox, когда выборtextarea
не возвращался обратно в исходное место после манипуляций с DOM. -
Я добавил поддержку IE7 - IE9 и исправил проблему выбора нескольких слов, указанную в комментариях.
-
Я добавил поддержку жестких возвратов, вставленных с помощью Enter и нескольких пробелов в строке.
-
Я исправил проблему с поведением по умолчанию для метода выбора текста ctrl + shift + left arrow.
JavaScript
function getTextAreaXandY() {
// Don't do anything if key pressed is left arrow
if (e.which == 37) return;
// Save selection start
var selection = $(this).getSelection();
var index = selection.start;
// Copy text to div
$(this).blur();
$("div").text($(this).val());
// Get current character
$(this).setSelection(index, index + 1);
currentcharacter = $(this).getSelection().text;
// Get previous character
$(this).setSelection(index - 1, index)
previouscharacter = $(this).getSelection().text;
var start, endchar;
var end = 0;
var range = rangy.createRange();
// If current or previous character is a space or a line break, find the next word and wrap it in a span
var linebreak = previouscharacter.match(/(\r\n|\n|\r)/gm) == undefined ? false : true;
if (previouscharacter == ' ' || currentcharacter == ' ' || linebreak) {
i = index + 1; // Start at the end of the current space
while (endchar != ' ' && end < $(this).val().length) {
i++;
$(this).setSelection(i, i + 1)
var sel = $(this).getSelection();
endchar = sel.text;
end = sel.start;
}
range.setStart($("div")[0].childNodes[0], index);
range.setEnd($("div")[0].childNodes[0], end);
var nextword = range.toHtml();
range.deleteContents();
var position = $("<span id='nextword'>" + nextword + "</span>")[0];
range.insertNode(position);
var nextwordtop = $("#nextword").position().top;
}
// Insert `#caret` at the position of the caret
range.setStart($("div")[0].childNodes[0], index);
var caret = $("<span id='caret'></span>")[0];
range.insertNode(caret);
var carettop = $("#caret").position().top;
// If preceding character is a space, wrap it in a span
if (previouscharacter == ' ') {
range.setStart($("div")[0].childNodes[0], index - 1);
range.setEnd($("div")[0].childNodes[0], index);
var prevchar = $("<span id='prevchar'></span>")[0];
range.insertNode(prevchar);
var prevchartop = $("#prevchar").position().top;
}
// Set textarea selection back to selection start
$(this).focus();
$(this).setSelection(index, selection.end);
// If the top value of the previous character span is not equal to the top value of the next word,
// there must have been some wrapping going on, the previous character was a space, so the wrapping
// would have occured after this space, its safe to assume that the left and top value of `#nextword`
// indicate the caret position
if (prevchartop != undefined && prevchartop != nextwordtop) {
$("label").text('X: ' + $("#nextword").position().left + 'px, Y: ' + $("#nextword").position().top);
$('ul').css('left', ($("#nextword").position().left) + 'px');
$('ul').css('top', ($("#nextword").position().top + 13) + 'px');
}
// if not, then there was no wrapping, we can take the left and the top value from `#caret`
else {
$("label").text('X: ' + $("#caret").position().left + 'px, Y: ' + $("#caret").position().top);
$('ul').css('left', ($("#caret").position().left) + 'px');
$('ul').css('top', ($("#caret").position().top + 14) + 'px');
}
$('ul').css('display', 'block');
}
$("textarea").click(getTextAreaXandY);
$("textarea").keyup(getTextAreaXandY);
HTML
<div></div>
<textarea>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</textarea>
<label></label>
<ul>
<li>Why don't you type this..</li>
</ul>
CSS
body {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
}
textarea, div {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
width: 300px;
display: block;
overflow: hidden;
border: 1px solid black;
padding: 0;
margin: 0;
resize: none;
min-height: 300px;
position: absolute;
-moz-box-sizing: border-box;
white-space: pre-wrap;
}
span {
display: inline-block;
height: 14px;
position: relative;
}
span#caret {
display: inline;
}
label {
display: block;
margin-left: 320px;
}
ul {
padding: 0px;
margin: 9px;
position: absolute;
z-index: 999;
border: 1px solid #000;
background-color: #FFF;
list-style-type:none;
display: none;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
span {
white-space: pre-wrap;
}
}
div {
/* Firefox wrapping fix */
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
/* IE8/IE9 wrapping fix */
padding-right: 5px\0/;
width: 295px\0/;
}
span#caret
{
display: inline-block\0/;
}