Comment obtenir la position de la colonne caret (pas des pixels) dans une zone de texte, en caractères, dès le début?

174

Comment obtenir la position du curseur dans un <textarea>JavaScript?

Par exemple: This is| a text

Cela devrait revenir 7.

Comment le feriez-vous pour renvoyer les chaînes entourant le curseur / la sélection?

Par exemple: 'This is', '', ' a text'.

Si le mot «est» est mis en surbrillance, il revient 'This ', 'is', ' a text'.

Wanderer agité
la source
Voir cette question: stackoverflow.com/questions/164147/… et si vous avez des nouvelles lignes, aussi la note à ce sujet ici: stackoverflow.com/questions/235411/…
bobince
1
Si vous utilisez jQuery, vous pouvez utiliser le plugin jquery caret $ ('textarea'). GetSelection (). Start plugins.jquery.com/plugin-tags/caret @ ++
redochka
J'ai trouvé une bonne solution sur blog.vishalon.net/index.php/ ... Je l'ai testé dans Firefox et Chrome, et cela a fonctionné dans les deux. L'auteur dit que cela fonctionne également dans IE + Opera.
pycoder112358
Utilisation simple textarea.selectionStart, textarea. selectionEnd, textarea.setSelectionRange developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement
EAndreyF

Réponses:

173

Avec Firefox, Safari (et d'autres navigateurs basés sur Gecko), vous pouvez facilement utiliser textarea.selectionStart, mais pour IE cela ne fonctionne pas, vous devrez donc faire quelque chose comme ceci:

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

( code complet ici )

Je vous recommande également de vérifier le plugin jQuery FieldSelection , il vous permet de faire cela et bien plus encore ...

Edit: J'ai en fait réimplémenté le code ci-dessus:

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

Consultez un exemple ici .

CMS
la source
1
Cela ne distingue pas les positions du curseur lorsque le curseur est placé sur une ligne vide. Voir stackoverflow.com/questions/3053542/…
Tim Down
4
Cette réponse ne traite pas du problème des lignes vides.
Tim Down
6
Comment puis-je utiliser ceci pour CONTENTEDITABLE div?
Muhammet Göktürk Ayan
1
Vous voudrez peut-être reformuler un peu cette réponse, cela Safari (and other Gecko based browsers)semble impliquer que Safari utilise Gecko. Gecko est le moteur de Mozilla; Safari utilise WebKit.
Code inutile
1
caret à la position 0 échouerait le test if (el.selectionStart) { return el.selectionStart; }...
okm
57

Mis à jour le 5 septembre 2010

Voyant que tout le monde semble être dirigé ici pour ce problème, j'ajoute ma réponse à une question similaire, qui contient le même code que cette réponse mais avec un historique complet pour ceux qui sont intéressés:

Document.selection.createRange d'IE n'inclut pas les lignes vierges de début ou de fin

Tenir compte des sauts de ligne de fin est délicat dans IE, et je n'ai vu aucune solution qui le fasse correctement, y compris d'autres réponses à cette question. Il est cependant possible d'utiliser la fonction suivante, qui vous renverra le début et la fin de la sélection (qui sont les mêmes dans le cas d'un signe d'insertion) dans un <textarea>texte ou <input>.

Notez que la zone de texte doit avoir le focus pour que cette fonction fonctionne correctement dans IE. En cas de doute, appelez d' focus()abord la méthode textarea .

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
la source
Excusez-moi mais que signifie « range && range.parentElement () »?
sergzach
Il y a un problème si nous voulons obtenir la position du curseur dans IE (si la sélection est vide). Dans ce cas, il renvoie 0 comme début et la longueur de chaîne comme fin (si nous utilisons true au lieu de range && range.parentElement () == el ).
sergzach
@sergzach: Le range && range.parentElement() == elest là pour tester si la sélection est dans la zone de texte et est nécessaire. Il n'y a aucun problème avec la fonction d'obtention de la position du curseur tant que la zone de texte a le focus . En cas de doute, appelez la focus()méthode de textarea avant d'appeler getInputSelection(). J'ajouterai une note à la réponse.
Tim Down
1
@Tim: Lorsque vous cliquez sur un élément div pour révéler la sélection, le "début" et la "fin" sont toujours les mêmes.
Misha Moroshko
3
@Misha: Ce n'est pas la faute de la fonction: c'est ce qui est effectivement sélectionné au moment où la fonction s'exécute. Vous pouvez le voir visuellement après avoir fermé la boîte d'alerte. Comme je l'ai mentionné dans ma réponse à votre question récente, deux solutions de contournement possibles sont l'utilisation de l' mousedownévénement ou l'ajout unselectable="on"à l' <div>élément.
Tim Down
3

J'ai modifié la fonction ci-dessus pour tenir compte des retours chariot dans IE. Il n'a pas été testé mais j'ai fait quelque chose de similaire dans mon code, donc cela devrait être réalisable.

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; 
}
marque
la source
2

Si vous ne devez pas prendre en charge IE, vous pouvez utiliser selectionStartet les selectionEndattributs de textarea.

Pour obtenir la position du curseur, utilisez simplement selectionStart:

function getCaretPosition(textarea) {
  return textarea.selectionStart
}

Pour obtenir les chaînes entourant la sélection, utilisez le code suivant:

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)]
}

Démo sur JSFiddle .

Voir aussi la documentation HTMLTextAreaElement .

Michał Perłakowski
la source