¿Cómo obtener la posición de la columna de intercalación (no píxeles) en un área de texto, en caracteres, desde el principio?

174

¿Cómo se obtiene la posición de intercalación en <textarea>JavaScript?

Por ejemplo: This is| a text

Esto debería volver 7.

¿Cómo conseguirías que devuelva las cadenas que rodean el cursor / selección?

Por ejemplo: 'This is', '', ' a text'.

Si la palabra "es" está resaltada, entonces volvería 'This ', 'is', ' a text'.

Errante inquieto
fuente
Vea esta pregunta: stackoverflow.com/questions/164147/… y si tendrá nuevas líneas, también la nota al respecto aquí: stackoverflow.com/questions/235411/…
bobince
1
Si está usando jQuery, puede usar jquery caret plugin $ ('textarea'). GetSelection (). Start plugins.jquery.com/plugin-tags/caret @ ++
redochka
Encontré una buena solución en blog.vishalon.net/index.php/... Lo probé en Firefox y Chrome, y funcionó en ambos. El escritor dice que también funciona en IE + Opera.
pycoder112358
Uso simple textarea.selectionStart, textarea. selectionEnd, textarea.setSelectionRange developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement
EAndreyF

Respuestas:

173

Con Firefox, Safari (y otros navegadores basados ​​en Gecko) puede usar fácilmente textarea.selectionStart, pero para IE eso no funciona, por lo que tendrá que hacer algo como esto:

function getCaret(node) {
  if (node.selectionStart) {
    return node.selectionStart;
  } else if (!document.selection) {
    return 0;
  }

  var c = "\001",
      sel = document.selection.createRange(),
      dul = sel.duplicate(),
      len = 0;

  dul.moveToElementText(node);
  sel.text = c;
  len = dul.text.indexOf(c);
  sel.moveStart('character',-1);
  sel.text = "";
  return len;
}

( código completo aquí )

También le recomiendo que consulte el complemento jQuery FieldSelection , que le permite hacer eso y mucho más ...

Editar: en realidad volví a implementar el código anterior:

function getCaret(el) { 
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
        rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    return rc.text.length; 
  }  
  return 0; 
}

Mira un ejemplo aquí .

CMS
fuente
1
Esto no distingue entre las posiciones del cursor cuando el cursor se coloca en una línea vacía. Ver stackoverflow.com/questions/3053542/…
Tim Down
44
Esta respuesta no trata el problema de las líneas vacías.
Tim Down
66
¿Cómo puedo usar esto para div CONTENTEDITABLE?
Muhammet Göktürk Ayan
1
Es posible que desee reformular un poco esta respuesta. Safari (and other Gecko based browsers)Parece implicar que Safari usa Gecko. Gecko es el motor de Mozilla; Safari usa WebKit.
Código inútil
1
caret en la posición 0 no pasaría la prueba if (el.selectionStart) { return el.selectionStart; }...
okm
57

Actualizado 5 de septiembre de 2010

Como parece que todos se dirigen aquí para este problema, estoy agregando mi respuesta a una pregunta similar, que contiene el mismo código que esta respuesta pero con antecedentes completos para aquellos que estén interesados:

El documento document.selection.createRange de IE no incluye líneas en blanco iniciales o finales

Tener en cuenta los saltos de línea finales es complicado en IE, y no he visto ninguna solución que lo haga correctamente, incluidas otras respuestas a esta pregunta. Sin embargo, es posible usar la siguiente función, que le devolverá el inicio y el final de la selección (que son los mismos en el caso de un cursor) dentro de un <textarea>texto <input>.

Tenga en cuenta que el área de texto debe tener foco para que esta función funcione correctamente en IE. En caso de duda, llame focus()primero al método del área de texto.

function getInputSelection(el) {
    var start = 0, end = 0, normalizedValue, range,
        textInputRange, len, endRange;

    if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
        start = el.selectionStart;
        end = el.selectionEnd;
    } else {
        range = document.selection.createRange();

        if (range && range.parentElement() == el) {
            len = el.value.length;
            normalizedValue = el.value.replace(/\r\n/g, "\n");

            // Create a working TextRange that lives only in the input
            textInputRange = el.createTextRange();
            textInputRange.moveToBookmark(range.getBookmark());

            // Check if the start and end of the selection are at the very end
            // of the input, since moveStart/moveEnd doesn't return what we want
            // in those cases
            endRange = el.createTextRange();
            endRange.collapse(false);

            if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
                start = end = len;
            } else {
                start = -textInputRange.moveStart("character", -len);
                start += normalizedValue.slice(0, start).split("\n").length - 1;

                if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
                    end = len;
                } else {
                    end = -textInputRange.moveEnd("character", -len);
                    end += normalizedValue.slice(0, end).split("\n").length - 1;
                }
            }
        }
    }

    return {
        start: start,
        end: end
    };
}
Tim Down
fuente
Disculpe, pero ¿qué significa ' range && range.parentElement () '?
sergzach
Hay un problema si queremos obtener la posición de caret en IE (si la selección está vacía). En este caso, devuelve 0 como inicio y longitud de cadena como final (si usamos verdadero en lugar de rango && range.parentElement () == el ).
sergzach
@sergzach: range && range.parentElement() == elestá ahí para probar si la selección está dentro del área de texto y si es necesaria. No hay problema con la función de obtener la posición de intercalación siempre que el área de texto tenga el foco . Si no está seguro, llame al focus()método del área de texto antes de llamar getInputSelection(). Agregaré una nota a la respuesta.
Tim Down
1
@Tim: Al hacer clic en un elemento div para revelar la selección, el "inicio" y el "final" son siempre los mismos.
Misha Moroshko
3
@Misha: Eso no es culpa de la función: eso es lo que realmente está seleccionado para cuando se ejecuta la función. Puede verlo visualmente después de cerrar el cuadro de alerta. Como mencioné en mi respuesta a su pregunta reciente, dos posibles soluciones son usar el mousedownevento o agregar unselectable="on"al <div>elemento.
Tim Down
3

Modifiqué la función anterior para tener en cuenta los retornos de carro en IE. No se ha probado, pero hice algo similar con él en mi código, por lo que debería ser viable.

function getCaret(el) {
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
    rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    var add_newlines = 0;
    for (var i=0; i<rc.text.length; i++) {
      if (rc.text.substr(i, 2) == '\r\n') {
        add_newlines += 2;
        i++;
      }
    }

    //return rc.text.length + add_newlines;

    //We need to substract the no. of lines
    return rc.text.length - add_newlines; 
  }  
  return 0; 
}
marca
fuente
2

Si no tiene que admitir IE, puede usar selectionStarty selectionEndatributos de textarea.

Para obtener la posición de cuidado simplemente use selectionStart:

function getCaretPosition(textarea) {
  return textarea.selectionStart
}

Para obtener las cadenas que rodean la selección, use el siguiente código:

function getSurroundingSelection(textarea) {
  return [textarea.value.substring(0, textarea.selectionStart)
         ,textarea.value.substring(textarea.selectionStart, textarea.selectionEnd)
         ,textarea.value.substring(textarea.selectionEnd, textarea.value.length)]
}

Demo en JSFiddle .

Ver también documentos HTMLTextAreaElement .

Michał Perłakowski
fuente