Como obter a posição da coluna de sinal de intercalação (não pixels) em uma área de texto, em caracteres, desde o início?

174

Como você obtém a posição do cursor em um <textarea> JavaScript usando?

Por exemplo: This is| a text

Isso deve retornar 7 .

Como você faria para retornar as strings ao redor do cursor / seleção?

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

Se a palavra "é" estiver destacada, ela retornará 'This ', 'is', ' a text'.

Andarilho Inquieto
fonte
Veja esta pergunta: stackoverflow.com/questions/164147/… e se você tiver novas linhas, também a observação sobre isso aqui: stackoverflow.com/questions/235411/…
bobince
1
Se você estiver usando o jQuery, poderá usar o plug-in de intercalação jquery $ ('textarea'). GetSelection (). Start plugins.jquery.com/plugin-tags/caret @ ++ #
redochka
Encontrei uma boa solução em blog.vishalon.net/index.php/…. Eu testei no firefox e chrome, e funcionou em ambos. O escritor diz que também funciona no IE + Opera.
pycoder112358
Uso simples textarea.selectionStart, textarea. selectionEnd, textarea.setSelectionRange developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement
EAndreyF

Respostas:

173

Com o Firefox, Safari (e outros navegadores baseados no Gecko), você pode facilmente usar textarea.selectionStart, mas para o IE que não funcionar, você precisará fazer algo assim:

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 aqui )

Também recomendo que você verifique o plug- in jQuery FieldSelection , ele permite que você faça isso e muito mais ...

Edit: Na verdade, reimplementei o código acima:

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

Veja um exemplo aqui .

CMS
fonte
1
Isso não faz distinção entre as posições do cursor quando o cursor é colocado em uma linha vazia. Veja stackoverflow.com/questions/3053542/…
Tim Down
4
Esta resposta não lida com o problema de linhas vazias.
Tim Baixo
6
Como posso usar isso para a div CONTENTEDITABLE?
Muhammet Göktürk Ayan
1
Você pode reformular um pouco esta resposta, Safari (and other Gecko based browsers)parece sugerir que o Safari usa o Gecko. Gecko é o motor da Mozilla; O Safari usa o WebKit.
Código inútil
1
acento circunflexo na posição 0 iria falhar o teste if (el.selectionStart) { return el.selectionStart; }...
OKM
57

Atualizado 5 de setembro de 2010

Visto que todo mundo parece ser direcionado aqui para esse problema, estou adicionando minha resposta a uma pergunta semelhante, que contém o mesmo código que esta resposta, mas com o histórico completo para aqueles que estão interessados:

O document.selection.createRange do IE não inclui linhas em branco à esquerda ou à direita

A explicação de quebras de linha à direita é complicada no IE, e não vi nenhuma solução que faça isso corretamente, incluindo outras respostas a essa pergunta. É possível, no entanto, usar a seguinte função, que retornará o início e o final da seleção (que são iguais no caso de um sinal de intercalação) dentro de um <textarea>texto ou<input> .

Observe que a área de texto deve ter foco para que essa função funcione corretamente no IE. Em caso de dúvida, chame o focus()método da área de texto primeiro.

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
fonte
Desculpe-me, mas o que significa ' range && range.parentElement () '?
sergzach
Existe um problema se queremos obter a posição do cursor no IE (se a seleção estiver vazia). Nesse caso, ele retorna 0 como início e o comprimento da string como final (se usarmos true em vez de range && range.parentElement () == el ).
sergzach
@ergzach: range && range.parentElement() == elExiste para testar se a seleção está dentro da área de texto e é necessária. Não há nenhum problema com a função de obter a posição do cursor , desde que a área de texto tenha o foco . Se não tiver certeza, chame o focus()método da área de texto antes de chamar getInputSelection(). Vou adicionar uma nota à resposta.
Tim Baixo
1
@ Tim: Ao clicar em um elemento div para revelar a seleção, o "início" e o "fim" são sempre os mesmos.
Misha Moroshko
3
@Misha: Isso não é culpa da função: é isso que é realmente selecionado no momento em que a função é executada. Você pode vê-lo visualmente depois de descartar a caixa de alerta. Como mencionei na minha resposta à sua pergunta recente, duas soluções possíveis estão usando o mousedownevento ou adicionando unselectable="on"ao <div>elemento.
Tim Baixo
3

Modifiquei a função acima para contabilizar retornos de carro no IE. Não foi testado, mas fiz algo semelhante com ele no meu código, portanto deve ser viável.

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
fonte
2

Se você não precisar oferecer suporte ao IE, poderá usar selectionStarte selectionEndatributos de textarea.

Para obter a posição do cursor, use selectionStart:

function getCaretPosition(textarea) {
  return textarea.selectionStart
}

Para obter as seqüências de caracteres ao redor da seleção, use o seguinte 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)]
}

Demonstração no JSFiddle .

Consulte também os documentos HTMLTextAreaElement .

Michał Perłakowski
fonte