javascript contenteditable caret cursor-position

javascript - Obtener la posición de interés en contentEditable div



caret cursor-position (6)

Estoy encontrando montones de buenos navegadores de navegador cruzado sobre cómo ESTABLECER el cursor o la posición de intercalación en un contenido DIV editable, pero ninguno sobre cómo OBTENER o encontrar su posición ...

Lo que quiero hacer es conocer la posición del cursor dentro de este div, en keyup.

Entonces, cuando el usuario está escribiendo texto, puedo saber en cualquier momento la posición de su cursor dentro del div.

EDITAR: estoy buscando el ÍNDICE dentro del contenido div (texto), no las coordenadas del cursor.

<div id="contentBox" contentEditable="true"></div> $(''#contentbox'').keyup(function() { // ... ? });


El siguiente código asume:

  • Siempre hay un único nodo de texto dentro del <div> editable y no hay otros nodos
  • El div editable no tiene la propiedad CSS white-space configurada para pre

Código:

function getCaretPosition(editableDiv) { var caretPos = 0, sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel.rangeCount) { range = sel.getRangeAt(0); if (range.commonAncestorContainer.parentNode == editableDiv) { caretPos = range.endOffset; } } } else if (document.selection && document.selection.createRange) { range = document.selection.createRange(); if (range.parentElement() == editableDiv) { var tempEl = document.createElement("span"); editableDiv.insertBefore(tempEl, editableDiv.firstChild); var tempRange = range.duplicate(); tempRange.moveToElementText(tempEl); tempRange.setEndPoint("EndToEnd", range); caretPos = tempRange.text.length; } } return caretPos; }

#caretposition { font-weight: bold; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div> <div id="caretposition">0</div> <script> var update = function() { $(''#caretposition'').html(getCaretPosition(this)); }; $(''#contentbox'').on("mousedown mouseup keydown keyup", update); </script>


Esta funciona para mí:

function getCaretCharOffsetInDiv(element) { var caretOffset = 0; if (typeof window.getSelection != "undefined") { var range = window.getSelection().getRangeAt(0); var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); caretOffset = preCaretRange.toString().length; } else if (typeof document.selection != "undefined" && document.selection.type != "Control") { var textRange = document.selection.createRange(); var preCaretTextRange = document.body.createTextRange(); preCaretTextRange.moveToElementText(element); preCaretTextRange.setEndPoint("EndToEnd", textRange); caretOffset = preCaretTextRange.text.length; } return caretOffset; }

la línea que llama depende del tipo de evento, para el evento clave use esto:

getCaretCharOffsetInDiv(e.target) + ($(window.getSelection().getRangeAt(0).startContainer.parentNode).index());

para el evento del mouse usa esto:

getCaretCharOffsetInDiv(e.target.parentElement) + ($(e.target).index())

en estos dos casos, me ocupo de las líneas divisorias añadiendo el índice objetivo



$("#editable").on(''keydown keyup mousedown mouseup'',function(e){ if($(window.getSelection().anchorNode).is($(this))){ $(''#position'').html(''0'') }else{ $(''#position'').html(window.getSelection().anchorOffset); } });

body{ padding:40px; } #editable{ height:50px; width:400px; border:1px solid #000; } #editable p{ margin:0; padding:0; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script> <div contenteditable="true" id="editable">move the cursor to see position</div> <div> position : <span id="position"></span> </div>


//global savedrange variable to store text range in var savedrange = null; function getSelection() { var savedRange; if(window.getSelection && window.getSelection().rangeCount > 0) //FF,Chrome,Opera,Safari,IE9+ { savedRange = window.getSelection().getRangeAt(0).cloneRange(); } else if(document.selection)//IE 8 and lower { savedRange = document.selection.createRange(); } return savedRange; } $(''#contentbox'').keyup(function() { var currentRange = getSelection(); if(window.getSelection) { //do stuff with standards based object } else if(document.selection) { //do stuff with microsoft object (ie8 and lower) } });

Nota: el objeto de rango se puede almacenar en una variable y se puede volver a seleccionar en cualquier momento, a menos que cambien los contenidos de la div contenteditable.

Referencia para IE 8 y versiones anteriores: http://msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx

Referencia para navegadores de estándares (todos los demás): https://developer.mozilla.org/en/DOM/range (son los documentos de mozilla, pero el código funciona en cromo, safari, opera y ie9 también)


function getCaretPosition() { var x = 0; var y = 0; var sel = window.getSelection(); if(sel.rangeCount) { var range = sel.getRangeAt(0).cloneRange(); if(range.getClientRects()) { range.collapse(true); var rect = range.getClientRects()[0]; if(rect) { y = rect.top; x = rect.left; } } } return { x: x, y: y }; }