N.B. Пожалуйста, обратитесь к моему другому ответу для лучшего решения, которое я могу предложить. Я оставляю это здесь для фона.
Я сталкивался с этой проблемой и написал следующее, которое работает во всех случаях. В IE он использует предложенный вами метод временной вставки символа на границе выделения, а затем использует document.execCommand("undo")
, чтобы удалить вставленный символ и предотвратить вставку в стек отмены. Я уверен, что нет более легкого пути. К счастью, IE 9 будет поддерживать свойства selectionStart
и selectionEnd
.
function getSelectionBoundary(el, isStart) {
var property = isStart ? "selectionStart" : "selectionEnd";
var originalValue, textInputRange, precedingRange, pos, bookmark;
if (typeof el[property] == "number") {
return el[property];
} else if (document.selection && document.selection.createRange) {
el.focus();
var range = document.selection.createRange();
if (range) {
range.collapse(!!isStart);
originalValue = el.value;
textInputRange = el.createTextRange();
precedingRange = textInputRange.duplicate();
pos = 0;
if (originalValue.indexOf("\r\n") > -1) {
// Trickier case where input value contains line breaks
// Insert a character in the text input range and use that as
// a marker
range.text = " ";
bookmark = range.getBookmark();
textInputRange.moveToBookmark(bookmark);
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length - 1;
// Executing an undo command to delete the character inserted
// prevents this method adding to the undo stack. This trick
// came from a user called Trenda on MSDN:
// http://msdn.microsoft.com/en-us/library/ms534676%28VS.85%29.aspx
document.execCommand("undo");
} else {
// Easier case where input value contains no line breaks
bookmark = range.getBookmark();
textInputRange.moveToBookmark(bookmark);
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length;
}
return pos;
}
}
return 0;
}
var el = document.getElementById("your_textarea");
var startPos = getSelectionBoundary(el, true);
var endPos = getSelectionBoundary(el, false);
alert(startPos + ", " + endPos);
UPDATE
Основываясь на предложенном Бобинсом подходе в комментариях, я создал следующее, что, кажется, работает хорошо. Некоторые заметки:
- Подход Бобинса проще и короче.
- Мой подход навязчив: он вносит изменения в значение ввода перед возвратом этих изменений, хотя видимого эффекта от этого нет.
- Мой подход имеет преимущество, заключающееся в том, что все операции выполняются внутри ввода. Подход bobince основан на создании диапазонов от начала тела до текущего выделения.
- Следствием 3. является то, что производительность катушек меняется в зависимости от позиции ввода в документе, а у моей - нет. Мои простые тесты показывают, что, когда ввод близко к началу документа, подход Бобинса значительно быстрее. Когда ввод идет после значительного фрагмента HTML, мой подход быстрее.
function getSelection(el) {
var start = 0, end = 0, normalizedValue, textInputRange, elStart;
var range = document.selection.createRange();
var bigNum = -1e8;
if (range && range.parentElement() == el) {
normalizedValue = el.value.replace(/\r\n/g, "\n");
start = -range.moveStart("character", bigNum);
end = -range.moveEnd("character", bigNum);
textInputRange = el.createTextRange();
range.moveToBookmark(textInputRange.getBookmark());
elStart = range.moveStart("character", bigNum);
// Adjust the position to be relative to the start of the input
start += elStart;
end += elStart;
// Correct for line breaks so that offsets are relative to the
// actual value of the input
start += normalizedValue.slice(0, start).split("\n").length - 1;
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
return {
start: start,
end: end
};
}
var el = document.getElementById("your_textarea");
var sel = getSelection(el);
alert(sel.start + ", " + sel.end);