61 votes

Afficher le DIV à la position du curseur dans le Textarea

Dans le cadre d'un de mes projets, j'aimerais fournir une complétion automatique pour une zone de texte spécifique. Un peu comme intellisense/omnicomplete. Mais pour cela, je dois connaître la position absolue du curseur afin de savoir où le DIV doit apparaître.

Il s'avère que c'est (presque j'espère) impossible à réaliser. Quelqu'un a t-il une idée précise pour résoudre ce problème ?

35voto

enobrev Points 10306

Version 2 de mon expérience Hacky

Cette nouvelle version fonctionne avec n'importe quelle police, qui peut être ajustée à la demande, et n'importe quelle taille de zone de texte.

Après avoir remarqué que certains d'entre vous essayent toujours de faire fonctionner ce système, j'ai décidé d'essayer une nouvelle approche. Mes résultats sont bien meilleurs cette fois-ci - du moins sur google chrome sous linux. Je n'ai plus de PC Windows à ma disposition, donc je ne peux tester que sur chrome / firefox sur Ubuntu. Mes résultats fonctionnent à 100% de manière cohérente sur Chrome, et disons quelque part autour de 70 - 80% sur Firefox, mais je n'imagine pas qu'il serait incroyablement difficile de trouver les incohérences.

Cette nouvelle version s'appuie sur un objet Canvas. Dans mon exemple En fait, je montre ce canevas, juste pour que vous puissiez le voir en action, mais cela pourrait très bien être fait avec un objet caché.

Il s'agit très certainement d'un hack, et je m'excuse d'avance pour mon code plutôt improvisé. Au moins, dans Google Chrome, cela fonctionne de manière cohérente, quelle que soit la police utilisée ou la taille de la zone de texte. J'ai utilisé Sam Saffron pour afficher les coordonnées du curseur (un div à fond gris). J'ai également ajouté un lien "Randomize", afin que vous puissiez le voir fonctionner avec différentes tailles et styles de polices et de zones de texte, et observer la mise à jour de la position du curseur à la volée. Je vous recommande de consulter la démo pleine page pour que vous puissiez mieux voir la toile d'accompagnement jouer le jeu.

Je vais résumer comment cela fonctionne ...

L'idée sous-jacente est que nous essayons de redessiner la zone de texte sur un canevas, aussi fidèlement que possible. Étant donné que le navigateur utilise le même moteur de polices pour les deux zones de texte, nous pouvons utiliser la fonctionnalité de mesure des polices du canevas pour déterminer où se trouvent les choses. À partir de là, nous pouvons utiliser les méthodes de canevas à notre disposition pour déterminer nos coordonnées.

Tout d'abord, nous ajustons notre toile pour qu'elle corresponde aux dimensions de la zone de texte. Cette opération n'a qu'un but visuel, car la taille du canevas ne fait pas vraiment de différence dans notre résultat. Étant donné que Canvas ne permet pas d'envelopper les mots, j'ai dû inventer (voler/emprunter/malaxer) un moyen de séparer les lignes pour qu'elles correspondent le mieux possible à la zone de texte. C'est là que vous devrez probablement faire le plus d'ajustements inter-navigateurs.

Après le retournement de mot, tout le reste n'est que maths de base. Nous avons divisé les lignes dans un tableau pour imiter le retour à la ligne, et maintenant nous voulons boucler à travers ces lignes et descendre jusqu'au point où notre sélection actuelle se termine. Pour ce faire, nous comptons simplement les caractères et une fois que nous dépassons selection.end nous savons que nous sommes allés assez loin. Multipliez le nombre de lignes jusqu'à ce point par la hauteur de ligne et vous avez un y coordonnées.

Le site x est très similaire, sauf que nous utilisons context.measureText . Tant que nous imprimons le bon nombre de caractères, cela nous donnera la largeur de la ligne qui est dessinée sur Canvas, qui se trouve se terminer après le dernier caractère écrit, qui est le caractère avant le caractère actuel. selection.end position.

Lorsque vous essayez de déboguer ce problème pour d'autres navigateurs, vous devez rechercher les endroits où les lignes ne se brisent pas correctement. Vous verrez à certains endroits que le dernier mot d'une ligne dans le canevas s'est enroulé sur la zone de texte ou vice-versa. Cela est dû à la façon dont le navigateur gère les retours à la ligne. Tant que le retournement dans le canevas correspond à celui de la zone de texte, votre curseur devrait être correct.

Je vais coller la source ci-dessous. Vous devriez pouvoir la copier et la coller, mais si vous le faites, je vous demande de télécharger votre propre copie de jquery-fieldselection au lieu d'utiliser celle qui se trouve sur mon serveur.

J'ai aussi augmenté une nouvelle démo ainsi que un violon .

Bonne chance !

<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="utf-8" />
        <title>Tooltip 2</title>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
        <script type="text/javascript" src="http://enobrev.info/cursor/js/jquery-fieldselection.js"></script>
        <style type="text/css">
            form {
                float: left;
                margin: 20px;
            }

            #textariffic {
                height: 400px;
                width: 300px;
                font-size: 12px;
                font-family: 'Arial';
                line-height: 12px;
            }

            #tip {
                width:5px;
                height:30px;
                background-color: #777;
                position: absolute;
                z-index:10000
            }

            #mock-text {
                float: left;
                margin: 20px;
                border: 1px inset #ccc;
            }

            /* way the hell off screen */
            .scrollbar-measure {
                width: 100px;
                height: 100px;
                overflow: scroll;
                position: absolute;
                top: -9999px;
            }

            #randomize {
                float: left;
                display: block;
            }
        </style>
        <script type="text/javascript">
            var oCanvas;
            var oTextArea;
            var $oTextArea;
            var iScrollWidth;

            $(function() {
                iScrollWidth = scrollMeasure();
                oCanvas      = document.getElementById('mock-text');
                oTextArea    = document.getElementById('textariffic');
                $oTextArea   = $(oTextArea);

                $oTextArea
                        .keyup(update)
                        .mouseup(update)
                        .scroll(update);

                $('#randomize').bind('click', randomize);

                update();
            });

            function randomize() {
                var aFonts      = ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Impact', 'Times New Roman', 'Verdana', 'Webdings'];
                var iFont       = Math.floor(Math.random() * aFonts.length);
                var iWidth      = Math.floor(Math.random() * 500) + 300;
                var iHeight     = Math.floor(Math.random() * 500) + 300;
                var iFontSize   = Math.floor(Math.random() * 18)  + 10;
                var iLineHeight = Math.floor(Math.random() * 18)  + 10;

                var oCSS = {
                    'font-family':  aFonts[iFont],
                    width:          iWidth + 'px',
                    height:         iHeight + 'px',
                    'font-size':    iFontSize + 'px',
                    'line-height':  iLineHeight + 'px'
                };

                console.log(oCSS);

                $oTextArea.css(oCSS);

                update();
                return false;
            }

            function showTip(x, y) {
                $('#tip').css({
                      left: x + 'px',
                      top: y + 'px'
                  });
            }

            // http://stackoverflow.com/a/11124580/14651
            // http://stackoverflow.com/a/3960916/14651

            function wordWrap(oContext, text, maxWidth) {
                var aSplit = text.split(' ');
                var aLines = [];
                var sLine  = "";

                // Split words by newlines
                var aWords = [];
                for (var i in aSplit) {
                    var aWord = aSplit[i].split('\n');
                    if (aWord.length > 1) {
                        for (var j in aWord) {
                            aWords.push(aWord[j]);
                            aWords.push("\n");
                        }

                        aWords.pop();
                    } else {
                        aWords.push(aSplit[i]);
                    }
                }

                while (aWords.length > 0) {
                    var sWord = aWords[0];
                    if (sWord == "\n") {
                        aLines.push(sLine);
                        aWords.shift();
                        sLine = "";
                    } else {
                        // Break up work longer than max width
                        var iItemWidth = oContext.measureText(sWord).width;
                        if (iItemWidth > maxWidth) {
                            var sContinuous = '';
                            var iWidth = 0;
                            while (iWidth <= maxWidth) {
                                var sNextLetter = sWord.substring(0, 1);
                                var iNextWidth  = oContext.measureText(sContinuous + sNextLetter).width;
                                if (iNextWidth <= maxWidth) {
                                    sContinuous += sNextLetter;
                                    sWord = sWord.substring(1);
                                }
                                iWidth = iNextWidth;
                            }
                            aWords.unshift(sContinuous);
                        }

                        // Extra space after word for mozilla and ie
                        var sWithSpace = (jQuery.browser.mozilla || jQuery.browser.msie) ? ' ' : '';
                        var iNewLineWidth = oContext.measureText(sLine + sWord + sWithSpace).width;
                        if (iNewLineWidth <= maxWidth) {  // word fits on current line to add it and carry on
                            sLine += aWords.shift() + " ";
                        } else {
                            aLines.push(sLine);
                            sLine = "";
                        }

                        if (aWords.length === 0) {
                            aLines.push(sLine);
                        }
                    }
                }
                return aLines;
            }

            // http://davidwalsh.name/detect-scrollbar-width
            function scrollMeasure() {
                // Create the measurement node
                var scrollDiv = document.createElement("div");
                scrollDiv.className = "scrollbar-measure";
                document.body.appendChild(scrollDiv);

                // Get the scrollbar width
                var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;

                // Delete the DIV
                document.body.removeChild(scrollDiv);

                return scrollbarWidth;
            }

            function update() {
                var oPosition  = $oTextArea.position();
                var sContent   = $oTextArea.val();
                var oSelection = $oTextArea.getSelection();

                oCanvas.width  = $oTextArea.width();
                oCanvas.height = $oTextArea.height();

                var oContext    = oCanvas.getContext("2d");
                var sFontSize   = $oTextArea.css('font-size');
                var sLineHeight = $oTextArea.css('line-height');
                var fontSize    = parseFloat(sFontSize.replace(/[^0-9.]/g, ''));
                var lineHeight  = parseFloat(sLineHeight.replace(/[^0-9.]/g, ''));
                var sFont       = [$oTextArea.css('font-weight'), sFontSize + '/' + sLineHeight, $oTextArea.css('font-family')].join(' ');

                var iSubtractScrollWidth = oTextArea.clientHeight < oTextArea.scrollHeight ? iScrollWidth : 0;

                oContext.save();
                oContext.clearRect(0, 0, oCanvas.width, oCanvas.height);
                oContext.font = sFont;
                var aLines = wordWrap(oContext, sContent, oCanvas.width - iSubtractScrollWidth);

                var x = 0;
                var y = 0;
                var iGoal = oSelection.end;
                aLines.forEach(function(sLine, i) {
                    if (iGoal > 0) {
                        oContext.fillText(sLine.substring(0, iGoal), 0, (i + 1) * lineHeight);

                        x = oContext.measureText(sLine.substring(0, iGoal + 1)).width;
                        y = i * lineHeight - oTextArea.scrollTop;

                        var iLineLength = sLine.length;
                        if (iLineLength == 0) {
                            iLineLength = 1;
                        }

                        iGoal -= iLineLength;
                    } else {
                        // after
                    }
                });
                oContext.restore();

                showTip(oPosition.left + x, oPosition.top + y);
            }

        </script>
    </head>
    <body>

        <a href="#" id="randomize">Randomize</a>

        <form id="tipper">
            <textarea id="textariffic">Aliquam urna. Nullam augue dolor, tincidunt condimentum, malesuada quis, ultrices at, arcu. Aliquam nunc pede, convallis auctor, sodales eget, aliquam eget, ligula. Proin nisi lacus, scelerisque nec, aliquam vel, dictum mattis, eros. Curabitur et neque. Fusce sollicitudin. Quisque at risus. Suspendisse potenti. Mauris nisi. Sed sed enim nec dui viverra congue. Phasellus velit sapien, porttitor vitae, blandit volutpat, interdum vel, enim. Cras sagittis bibendum neque. Proin eu est. Fusce arcu. Aliquam elit nisi, malesuada eget, dignissim sed, ultricies vel, purus. Maecenas accumsan diam id nisi.

Phasellus et nunc. Vivamus sem felis, dignissim non, lacinia id, accumsan quis, ligula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed scelerisque nulla sit amet mi. Nulla consequat, elit vitae tempus vulputate, sem libero rhoncus leo, vulputate viverra nulla purus nec turpis. Nam turpis sem, tincidunt non, congue lobortis, fermentum a, ipsum. Nulla facilisi. Aenean facilisis. Maecenas a quam eu nibh lacinia ultricies. Morbi malesuada orci quis tellus.

Sed eu leo. Donec in turpis. Donec non neque nec ante tincidunt posuere. Pellentesque blandit. Ut vehicula vestibulum risus. Maecenas commodo placerat est. Integer massa nunc, luctus at, accumsan non, pulvinar sed, odio. Pellentesque eget libero iaculis dui iaculis vehicula. Curabitur quis nulla vel felis ullamcorper varius. Sed suscipit pulvinar lectus.</textarea>

        </form>

        <div id="tip"></div>

        <canvas id="mock-text"></canvas>
    </body>
</html>

Bug

Il y a un bug dont je me souviens. Si vous placez le curseur avant la première lettre d'une ligne, il affiche la "position" de la dernière lettre de la ligne précédente. Cela a à voir avec le fonctionnement de la sélection et de la fin. Je pense qu'il ne devrait pas être trop difficile de rechercher ce cas et de le corriger en conséquence.


Version 1

Je laisse ça ici pour que vous puissiez voir la progression sans avoir à fouiller dans l'historique des modifications.

Ce n'est pas parfait et c'est certainement un hack, mais j'ai réussi à le faire fonctionner assez bien sur WinXP IE, FF, Safari, Chrome et Opera.

Pour autant que je sache, il n'y a aucun moyen de connaître directement les coordonnées x/y d'un curseur sur un navigateur. Le site Méthode IE , mentionné par Adam Bellaire est intéressant, mais malheureusement, il n'est pas compatible avec tous les navigateurs. Je me suis dit que la meilleure solution suivante serait d'utiliser les caractères comme une grille.

Malheureusement, aucune information sur la métrique des polices n'est intégrée dans les navigateurs, ce qui signifie qu'une police monospace est le seul type de police dont la mesure est cohérente. De plus, il n'existe aucun moyen fiable de calculer la largeur d'une police à partir de sa hauteur. J'ai d'abord essayé d'utiliser un pourcentage de la hauteur, ce qui a bien fonctionné. Puis, j'ai modifié la taille de la police et tout est parti en vrille.

J'ai essayé une méthode pour déterminer la largeur des caractères, qui consistait à créer un textarea temporaire et à continuer à ajouter des caractères jusqu'à ce que le scrollHeight (ou scrollWidth) change. Cela semble plausible, mais à mi-chemin, je me suis rendu compte que je pouvais simplement utiliser l'attribut cols sur le textarea et j'ai pensé qu'il y avait assez d'astuces dans cette épreuve pour en ajouter une autre. Cela signifie que vous ne pouvez pas définir la largeur de la zone de texte via le CSS. Vous DEVEZ utiliser l'attribut cols pour que cela fonctionne.

Le problème suivant que j'ai rencontré est que, même lorsque vous définissez la police via css, les navigateurs signalent la police différemment. Quand vous ne définissez pas de police, Mozilla utilise monospace par défaut, IE utilise Courier New , Opéra "Courier New" (avec des citations), Safari, 'Lucida Grand' (avec des guillemets simples). Lorsque vous définissez la police à monospace mozilla et ie prennent ce que vous leur donnez, Safari sort en tant que -webkit-monospace et Opera reste avec "Courier New" .

Donc maintenant nous initialisons quelques variables. Assurez-vous de définir votre hauteur de ligne dans le CSS également. Firefox indique la hauteur de ligne correcte, mais IE indiquait "normal" et je n'ai pas pris la peine d'utiliser les autres navigateurs. J'ai simplement défini la hauteur de ligne dans mon fichier css et cela a résolu la différence. Je n'ai pas testé l'utilisation d'ems au lieu de pixels. La hauteur de ligne est juste la taille de la police. Vous devriez probablement la prédéfinir dans votre css également.

Aussi, un autre pré-réglage avant de commencer à placer les caractères - qui m'a vraiment fait me gratter la tête. Pour ie et mozilla, les caractères des zones de texte sont < cols, tout le reste est <= caractères. Donc Chrome peut contenir 50 caractères, mais mozilla et ie casseraient le dernier mot de la ligne.

Maintenant, nous allons créer un tableau des positions du premier caractère pour chaque ligne. Nous parcourons en boucle chaque caractère du textarea. Si c'est une nouvelle ligne, nous ajoutons une nouvelle position à notre tableau de lignes. S'il s'agit d'un espace, nous essayons de déterminer si le "mot" actuel peut tenir sur la ligne où nous nous trouvons ou s'il doit être repoussé à la ligne suivante. La ponctuation compte comme une partie du "mot". Je n'ai pas testé avec les tabulations, mais il y a une ligne pour ajouter 4 caractères pour une tabulation.

Une fois que nous avons un tableau de positions de lignes, nous bouclons et essayons de trouver sur quelle ligne se trouve le curseur. Nous utilisons la "fin" de la sélection comme curseur.

x = (position du curseur - première position du caractère de la ligne du curseur) * largeur du caractère

y = ((ligne du curseur + 1) * hauteur de la ligne) - position du curseur

J'utilise jquery 1.2.6 , Sélection des champs de jonction et jquery-dimensions

La démo : http://enobrev.info/cursor/

Et le code :

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Tooltip</title>
        <script type="text/javascript" src="js/jquery-1.2.6.js"></script>
        <script type="text/javascript" src="js/jquery-fieldselection.js"></script>
        <script type="text/javascript" src="js/jquery.dimensions.js"></script>
        <style type="text/css">
            form {
                margin: 20px auto;
                width: 500px;
            }

            #textariffic {
                height: 400px;
                font-size: 12px;
                font-family: monospace;
                line-height: 15px;
            }

            #tip {
                position: absolute;
                z-index: 2;
                padding: 20px;
                border: 1px solid #000;
                background-color: #FFF;
            }
        </style>
        <script type="text/javascript">
            $(function() {
                $('textarea')
                    .keyup(update)
                    .mouseup(update)
                    .scroll(update);
            });

            function showTip(x, y) {                
                y = y + $('#tip').height();

                $('#tip').css({
                    left: x + 'px',
                    top: y + 'px'
                });
            }

            function update() {
                var oPosition = $(this).position();
                var sContent = $(this).val();

                var bGTE = jQuery.browser.mozilla || jQuery.browser.msie;

                if ($(this).css('font-family') == 'monospace'           // mozilla
                ||  $(this).css('font-family') == '-webkit-monospace'   // Safari
                ||  $(this).css('font-family') == '"Courier New"') {    // Opera
                    var lineHeight   = $(this).css('line-height').replace(/[^0-9]/g, '');
                        lineHeight   = parseFloat(lineHeight);
                    var charsPerLine = this.cols;
                    var charWidth    = parseFloat($(this).innerWidth() / charsPerLine);

                    var iChar = 0;
                    var iLines = 1;
                    var sWord = '';

                    var oSelection = $(this).getSelection();
                    var aLetters = sContent.split("");
                    var aLines = [];

                    for (var w in aLetters) {
                        if (aLetters[w] == "\n") {
                            iChar = 0;
                            aLines.push(w);
                            sWord = '';
                        } else if (aLetters[w] == " ") {    
                            var wordLength = parseInt(sWord.length);

                            if ((bGTE && iChar + wordLength >= charsPerLine)
                            || (!bGTE && iChar + wordLength > charsPerLine)) {
                                iChar = wordLength + 1;
                                aLines.push(w - wordLength);
                            } else {                
                                iChar += wordLength + 1; // 1 more char for the space
                            }

                            sWord = '';
                        } else if (aLetters[w] == "\t") {
                            iChar += 4;
                        } else {
                            sWord += aLetters[w];     
                        }
                    }

                    var iLine = 1;
                    for(var i in aLines) {
                        if (oSelection.end < aLines[i]) {
                            iLine = parseInt(i) - 1;
                            break;
                        }
                    }

                    if (iLine > -1) {
                        var x = parseInt(oSelection.end - aLines[iLine]) * charWidth;
                    } else {
                        var x = parseInt(oSelection.end) * charWidth;
                    }
                    var y = (iLine + 1) * lineHeight - this.scrollTop; // below line

                    showTip(oPosition.left + x, oPosition.top + y);
                }
            }

        </script>
    </head>
    <body>
        <form id="tipper">
            <textarea id="textariffic" cols="50">
Aliquam urna. Nullam augue dolor, tincidunt condimentum, malesuada quis, ultrices at, arcu. Aliquam nunc pede, convallis auctor, sodales eget, aliquam eget, ligula. Proin nisi lacus, scelerisque nec, aliquam vel, dictum mattis, eros. Curabitur et neque. Fusce sollicitudin. Quisque at risus. Suspendisse potenti. Mauris nisi. Sed sed enim nec dui viverra congue. Phasellus velit sapien, porttitor vitae, blandit volutpat, interdum vel, enim. Cras sagittis bibendum neque. Proin eu est. Fusce arcu. Aliquam elit nisi, malesuada eget, dignissim sed, ultricies vel, purus. Maecenas accumsan diam id nisi.

Phasellus et nunc. Vivamus sem felis, dignissim non, lacinia id, accumsan quis, ligula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed scelerisque nulla sit amet mi. Nulla consequat, elit vitae tempus vulputate, sem libero rhoncus leo, vulputate viverra nulla purus nec turpis. Nam turpis sem, tincidunt non, congue lobortis, fermentum a, ipsum. Nulla facilisi. Aenean facilisis. Maecenas a quam eu nibh lacinia ultricies. Morbi malesuada orci quis tellus.

Sed eu leo. Donec in turpis. Donec non neque nec ante tincidunt posuere. Pellentesque blandit. Ut vehicula vestibulum risus. Maecenas commodo placerat est. Integer massa nunc, luctus at, accumsan non, pulvinar sed, odio. Pellentesque eget libero iaculis dui iaculis vehicula. Curabitur quis nulla vel felis ullamcorper varius. Sed suscipit pulvinar lectus. 
            </textarea>

        </form>

        <p id="tip">Here I Am!!</p>
    </body>
</html>

4voto

prike Points 49

J'ai publié un sujet lié à ce problème sur un site JavaScript russe.

Si vous ne comprenez pas le russe, essayez la version traduite par Google : http://translate.google.ru/translate?js=y&prev=_t&hl=ru&ie=UTF-8&layout=1&eotf=1&u=http://javascript.ru/forum/events/7771-poluchit-koordinaty-kursora-v-tekstovom-pole-v-pikselyakh.html&sl=ru&tl=en

Il y a quelques problèmes de balisage dans les exemples de code de la version traduite, vous pouvez donc lire le code dans le post russe original .

L'idée est simple. Il n'existe pas de méthode simple, universelle et multi-navigateurs pour obtenir la position du curseur en pixels. Pour être franc, il existe une méthode, mais uniquement pour Internet Explorer.

Dans d'autres navigateurs, si vous avez vraiment besoin de le calculer, vous devez ...

  • créer un DIV invisible
  • copier tous les styles et le contenu de la zone de texte dans ce DIV
  • puis insérer l'élément HTML exactement à la même position dans le texte où se trouve le signe d'insertion dans la zone de texte.
  • obtenir les coordonnées de cet élément HTML

4voto

Marlon Jerez Isla Points 623

Je ne vais pas réexpliquer les problèmes liés à ce matériel car ils sont bien expliqués dans d'autres posts. Je vais juste indiquer une solution possible, elle comporte quelques bugs mais c'est un point de départ.

Heureusement, il existe un script sur Github qui permet de calculer la position du signe d'insertion par rapport à son conteneur, mais il nécessite jQuery. Page GitHub ici : jquery-caret-position-getter, Merci à Bevis.Zhao.

Sur cette base, j'ai implémenté le code suivant : regardez-le en action ici dans jsFiddle.net

<html><head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>- jsFiddle demo by mjerez</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.js"></script>
    <link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/normalize.css">
    <link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/result-light.css">   
    <script type="text/javascript" src="https://raw.github.com/beviz/jquery-caret-position-getter/master/jquery.caretposition.js"></script>     
    <style type="text/css">
        body{position:relative;font:normal 100% Verdana, Geneva, sans-serif;padding:10px;}
        .aux{background:#ccc;opacity: 0.5;width:50%;padding:5px;border:solid 1px #aaa;}
        .hidden{display:none}
        .show{display:block; position:absolute; top:0px; left:0px;}
    </style>
    <script type="text/javascript">//<![CDATA[ 
    $(document).keypress(function(e) {
        if ($(e.target).is('input, textarea')) {
            var key = String.fromCharCode(e.which);
            var ctrl = e.ctrlKey;
            if (ctrl) {
                var display = $("#autocomplete");
                var editArea = $('#editArea');            
                var pos = editArea.getCaretPosition();
                var offset = editArea.offset();
                // now you can use left, top(they are relative position)
                display.css({
                    left: offset.left + pos.left,
                    top:  offset.top + pos.top,
                    color : "#449"
                })
                display.toggleClass("show");
                return false;
            }
        }

    });
    window.onload = (function() {
        $("#editArea").blur(function() {
            if ($("#autocomplete").hasClass("show")) $("#autocomplete").toggleClass("show");
        })
    });
    //]]>  
    </script>
</head>
<body>
    <p>Click ctrl+space to while you write to diplay the autocmplete pannel.</p>
    </br>
    <textarea id="editArea" rows="4" cols="50"></textarea>
    </br>
    </br>
    </br>
    <div id="autocomplete" class="aux hidden ">
        <ol>
            <li>Option a</li>
            <li>Option b</li>
            <li>Option c</li>
            <li>Option d</li>
        </ol>
    </div>
</body>

4voto

Dan Dascalescu Points 8165

Notez que cette question est un doublon d'une question posée un mois plus tôt, à laquelle j'ai répondu. ici . Je ne maintiendrai que la réponse à ce lien, puisque cette question aurait dû être classée comme dupliquée il y a des années.

Copie de la réponse

J'ai cherché un plugin de coordonnées de caret pour les zones de texte. meteor-autocomplete J'ai donc évalué les 8 plugins sur GitHub. Le gagnant est, de loin, textarea-caret-position de Composant .

Caractéristiques

  • précision des pixels
  • aucune dépendance
  • compatibilité des navigateurs : Chrome, Safari, Firefox (malgré deux bogues ), IE9+ ; peut fonctionner mais n'a pas été testé dans Opera, IE8 ou plus ancien.
  • prend en charge toutes les familles et tailles de polices, ainsi que les transformations de texte.
  • la zone de texte peut avoir un rembourrage ou des bordures arbitraires
  • pas de confusion avec les barres de défilement horizontales ou verticales dans la zone de texte
  • supporte les retours en force, les tabulations (sauf sur IE) et les espaces consécutifs dans le texte
  • position correcte sur les lignes plus longues que les colonnes de la zone de texte
  • pas de position "fantôme" dans l'espace vide à la fin d'une ligne lors de la mise en forme de longs mots

Voici une démo - http://jsfiddle.net/dandv/aFPA7/

enter image description here

Comment cela fonctionne

Un miroir <div> est créé hors écran et stylisé exactement comme l'élément <textarea> . Ensuite, le texte de la zone de texte jusqu'à l'astérisque est copié dans le div et une icône <span> est inséré juste après. Ensuite, le contenu du texte du span est défini comme le reste du texte de la zone de texte, afin de reproduire fidèlement l'habillage de la fausse div.

Il s'agit de la seule méthode garantissant la prise en charge de tous les cas limites liés à l'enveloppement de longues lignes. Elle est également utilisée par GitHub pour déterminer la position de sa balise @ la liste déroulante des utilisateurs.

1voto

snoopy-do Points 342

Ce site blog semble être proche de la réponse à la question. Je ne l'ai pas essayé moi-même, mais l'auteur dit qu'il a été testé avec FF3, Chrome, IE, Opera, Safari. Le code est sur GitHub

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X