Obtener el desplazamiento inicial y final de un rango en relación con su contenedor principal
Supongamos que tengo este elemento HTML:
<div id="parent">
Hello everyone! <a>This is my home page</a>
<p>Bye!</p>
</div>
Y el usuario selecciona "inicio" con el mouse.
Quiero poder determinar cuántos personajes #parent
comienzan en su selección (y cuántos personajes #parent
terminan al final de su selección). Esto debería funcionar incluso si selecciona una etiqueta HTML. (Y necesito que funcione en todos los navegadores)
range.startOffset
parece prometedor, pero es un desplazamiento relativo sólo al contenedor inmediato del rango, y es un desplazamiento de caracteres sólo si el contenedor es un nodo de texto.
ACTUALIZAR
Como se señaló en los comentarios, mi respuesta original (a continuación) solo devuelve el final de la selección o la posición del cursor. Es bastante fácil adaptar el código para devolver un desplazamiento inicial y final; aquí hay un ejemplo que lo hace:
Mostrar fragmento de código
Aquí hay una función que obtendrá el desplazamiento del carácter del signo de intercalación dentro del elemento especificado; sin embargo, esta es una implementación ingenua que casi seguramente tendrá inconsistencias con los saltos de línea y no intenta lidiar con el texto oculto a través de CSS (sospecho que IE ignorará correctamente dicho texto mientras que otros navegadores no lo harán). Manejar todo esto adecuadamente sería complicado. Ahora lo intenté para mi biblioteca Rangy .
Ejemplo en vivo: http://jsfiddle.net/TjXEG/900/
function getCaretCharacterOffsetWithin(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ( (sel = doc.selection) && sel.type != "Control") {
var textRange = sel.createRange();
var preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
Después de experimentar unos días encontré un enfoque que parece prometedor. Como selectNodeContents()
no maneja <br>
las etiquetas correctamente, escribí un algoritmo personalizado para determinar la longitud del texto de cada una node
dentro de un archivo contenteditable
. Para calcular, por ejemplo, el inicio de la selección, sumo las longitudes del texto de todos los nodos anteriores. De esa manera, puedo manejar (múltiples) saltos de línea:
Mostrar fragmento de código
Sé que esto tiene un año, pero esta publicación es uno de los principales resultados de búsqueda para muchas preguntas sobre cómo encontrar la posición Caret y la encontré útil.
Estaba intentando usar el excelente script de Tim anterior para encontrar la nueva posición del cursor después de haber arrastrado y soltado un elemento de una posición a otra en un div de contenido editable. Funcionó perfectamente en FF e IE, pero en Chrome, la acción de arrastrar resaltó todo el contenido entre el principio y el final del arrastre, lo que resultó en que el resultado devuelto caretOffset
fuera demasiado grande o pequeño (según la longitud del área seleccionada).
Agregué algunas líneas a la primera declaración if para verificar si se seleccionó el texto y ajustar el resultado en consecuencia. La nueva declaración está a continuación. Perdóneme si es inapropiado agregar esto aquí, ya que no es lo que el OP intentaba hacer, pero como dije, varias búsquedas de información relacionada con la posición de Caret me llevaron a esta publicación, por lo que (con suerte) es probable que ayude a alguien más. .
Primera declaración if de Tim con líneas agregadas (*):
if (typeof window.getSelection != "undefined") {
var range = window.getSelection().getRangeAt(0);
var selected = range.toString().length; // *
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length - selected; // *
}