Como definir a posição do cursor (cursor) no elemento contenteditable (div)?

Eu tenho este HTML simples como um exemplo:

text text text
text text text
text text text

Eu quero uma coisa simples – quando clico no botão, quero colocar o cursor (cursor) em um lugar específico na div editável. A partir da pesquisa na Web, eu tenho esse JS anexado ao clique do botão, mas ele não funciona (FF, Chrome):

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

É possível definir manualmente a posição do cursor como esta?

Na maioria dos navegadores, você precisa dos objects Range e Selection . Você especifica cada um dos limites de seleção como um nó e um deslocamento dentro desse nó. Por exemplo, para definir o cursor para o quinto caractere da segunda linha do texto, você faria o seguinte:

 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 <9 funciona de forma completamente diferente. Se você precisar dar suporte a esses navegadores, precisará de um código diferente.

Exemplo do jsFiddle: http://jsfiddle.net/timdown/vXnCM/

A maioria das respostas que você encontra no posicionamento do cursor de conteúdo contíguo é bastante simplista, pois elas servem apenas para inputs com texto simples de baunilha. Depois de usar elementos html dentro do contêiner, o texto inserido é dividido em nós e distribuído livremente por uma estrutura de tree.

Para definir a posição do cursor, eu tenho essa function que faz um loop em torno de todos os nós de texto filho dentro do nó fornecido e define um intervalo desde o início do nó inicial até o caractere chars.count :

 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; }; 

Eu então chamo a rotina com esta function:

 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); } } }; 

O range.collapse (false) define o cursor para o final do intervalo. Eu testei com as versões mais recentes do Chrome, IE, Mozilla e Opera e todas funcionam bem.

PS. Se alguém estiver interessado, obtenho a posição atual do cursor usando este código:

 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; }; 

O código faz o oposto da function set - obtém o atual window.getSelection (). FocusNode e focusOffset e conta de trás para frente todos os caracteres de texto encontrados até atingir um nó pai com id de containerId. A function isChildOf apenas verifica antes de executar que o nó fornecido é, na verdade, um filho do parentId fornecido.

O código deve funcionar direito sem alterações, mas eu acabei de tirá-lo de um plugin jQuery que desenvolvi, então hackeei alguns deles - deixe-me saber se algo não funciona!

É muito difícil definir circunflexo na posição correta quando você tem elemento de avanço como (p) (span) etc. O objective é obter (texto do object):

  
dddddddddddddddddddddddddddd

dd

psss

dd

dd

text text text

Se você não quiser usar o jQuery, você pode tentar esta abordagem:

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

editableDiv você elemento editável, não se esqueça de definir um id para isso. Então você precisa obter o seu innerHTML do elemento e cortar todas as linhas de freio. E apenas defina o colapso com os próximos argumentos.