Как установить положение курсора в элементе contenteditable(div)?


у меня есть этот простой HTML в качестве примера:

<div id="editable" contenteditable="true">
  text text text<br>
  text text text<br>
  text text text<br>
</div>
<button id="button">focus</button>

Я хочу простую вещь-когда я нажимаю кнопку, Я хочу поместить курсор(курсор) в определенное место в редактируемом div. Из поиска в интернете у меня есть этот JS, прикрепленный к кнопке click, но он не работает (FF, Chrome):

var range = document.createRange();
var myDiv = document.getElementById("editable");
range.setStart(myDiv, 5);
range.setEnd(myDiv, 5);

можно ли установить вручную положение каретки, как это ?

5 142

5 ответов:

в большинстве браузеров, вам нужно Range и Selection объекты. Вы указываете каждую из границ выбора как узел и смещение внутри этого узла. Например, чтобы установить курсор на пятый символ второй строки текста, вы должны сделать следующее:

var el = document.getElementById("editable");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[2], 5);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

IE

пример jsFiddle : http://jsfiddle.net/timdown/vXnCM/

большинство ответов, которые вы найдете на contenteditable позиционирование курсора довольно упрощены в том, что они обслуживают только входы с простым текстом ванили. Как только вы используете html-элементы в контейнере, введенный текст разбивается на узлы и распределяется либерально по древовидной структуре.

для установки позиции курсора у меня есть эта функция, которая обходит все дочерние текстовые узлы в пределах предоставленного узла и устанавливает диапазон от начала начального узла до chars.граф символ:

function createRange(node, chars, range) {
    if (!range) {
        range = document.createRange()
        range.selectNode(node);
        range.setStart(node, 0);
    }

    if (chars.count === 0) {
        range.setEnd(node, chars.count);
    } else if (node && chars.count >0) {
        if (node.nodeType === Node.TEXT_NODE) {
            if (node.textContent.length < chars.count) {
                chars.count -= node.textContent.length;
            } else {
                range.setEnd(node, chars.count);
                chars.count = 0;
            }
        } else {
           for (var lp = 0; lp < node.childNodes.length; lp++) {
                range = createRange(node.childNodes[lp], chars, range);

                if (chars.count === 0) {
                    break;
                }
            }
        }
    } 

    return range;
};

затем я вызываю процедуру с этой функцией:

function setCurrentCursorPosition(chars) {
    if (chars >= 0) {
        var selection = window.getSelection();

        range = createRange(document.getElementById("test").parentNode, { count: chars });

        if (range) {
            range.collapse(false);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
};

диапазоне.свернуть (false) устанавливает курсор в конец диапазона. Я протестировал его с последними версиями Chrome, IE, Mozilla и Opera, и все они работают нормально.

PS. Если кто-то заинтересован, я получаю текущую позицию курсора, используя этот код:

function isChildOf(node, parentId) {
    while (node !== null) {
        if (node.id === parentId) {
            return true;
        }
        node = node.parentNode;
    }

    return false;
};

function getCurrentCursorPosition(parentId) {
    var selection = window.getSelection(),
        charCount = -1,
        node;

    if (selection.focusNode) {
        if (isChildOf(selection.focusNode, parentId)) {
            node = selection.focusNode; 
            charCount = selection.focusOffset;

            while (node) {
                if (node.id === parentId) {
                    break;
                }

                if (node.previousSibling) {
                    node = node.previousSibling;
                    charCount += node.textContent.length;
                } else {
                     node = node.parentNode;
                     if (node === null) {
                         break
                     }
                }
           }
      }
   }

    return charCount;
};

код делает противоположность функции set-он получает текущий окно.getSelection().focusNode и focusOffset и отсчитывает назад все текстовые символы, встречающиеся до тех пор, пока он не попадает в родительский узел с идентификатором containerId. Функция isChildOf просто проверяет перед запуском, что suplied узел на самом деле является дочерним элементом поставляемого parentId.

код должен работать прямо без изменений, но я только что взял его из плагина jQuery, который я разработал, поэтому взломал пару это - дайте мне знать, если что-то не работай!

это очень трудно установить каретку в правильном положении, когда у вас есть предварительный элемент, такой как (p) (span) и т. д.Цель состоит в том, чтобы получить (текст объекта):

     <div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p><p>dd</p>
    <p>text text text</p>
     </div>
    <p id='we'></p>
    <button onclick="set_mouse()">focus</button>
     <script>

function set_mouse() {
    var as = document.getElementById("editable");
    el=as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text) because it work only in object text
  var range = document.createRange();
     var sel = window.getSelection();
range.setStart(el, 1);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

document.getElementById("we").innerHTML=el;// see out put of we id
}
</script>

если вы не хотите использовать jQuery, вы можете попробовать этот подход:

public setCaretPosition() {
    const editableDiv = document.getElementById('contenteditablediv');
    const lastLine = this.input.nativeElement.innerHTML.replace(/.*?(<br>)/g, '');
    const selection = window.getSelection();
    selection.collapse(editableDiv.childNodes[editableDiv.childNodes.length - 1], lastLine.length);
}

editableDiv вы редактируемый элемент, не забудьте установить id для него. Тогда вам нужно innerHTML от элемента и отрежьте все тормозные магистрали. И просто установите коллапс со следующими аргументами.

Я пишу синтаксический маркер (и базовый редактор кода), и мне нужно было знать, как автоматически вводить символ одной кавычки и перемещать курсор назад (как и многие редакторы кода в настоящее время).

вот фрагмент моего решения, благодаря большой помощи из этого потока, MDN docs и большому количеству консолей moz..

//onKeyPress event

if (evt.key === "\"") {
    let sel = window.getSelection();
    let offset = sel.focusOffset;
    let focus = sel.focusNode;

    focus.textContent += "\""; //setting div's innerText directly creates new
    //nodes, which invalidate our selections, so we modify the focusNode directly

    let range = document.createRange();
    range.selectNode(focus);
    range.setStart(focus, offset);

    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
}

//end onKeyPress event

это в contenteditable div элемент

Я оставляю это здесь в качестве благодарности, понимая, что уже есть принятый ответ.