Obtener el desplazamiento inicial y final de un rango en relación con su contenedor principal

Resuelto Tom Lehman asked hace 13 años • 4 respuestas

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 #parentcomienzan en su selección (y cuántos personajes #parentterminan 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.startOffsetparece 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.

Tom Lehman avatar Jan 27 '11 07:01 Tom Lehman
Aceptado

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;
}
Tim Down avatar Jan 27 '2011 01:01 Tim Down

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 nodedentro 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

Candor avatar Jan 24 '2019 17:01 Candor

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 caretOffsetfuera 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; // *
}
Cody Crumrine avatar Sep 19 '2012 18:09 Cody Crumrine