Показать DIV в позиции курсора в Textarea [дубликат]

На этот вопрос уже есть ответ здесь:

Как получить (x, y) пиксельные координаты каретки в текстовых полях? 3 ответа

Для моего проекта я бы хотел предоставить автозаполнение для конкретной текстовой области. Аналогично тому, как работает intellisense / omnicomplete. Для этого, однако, я должен выяснить абсолютную позицию курсора, чтобы я знал, где должен появиться DIV.

Получается что's (почти я надеюсь) невозможно достичь. У кого-нибудь есть четкие идеи, как решить эту проблему?

 Sam Saffron28 окт. 2012 г., 01:00
приближаемся к хрому:jsbin.com/egadoj/1/edit
 Sam Saffron24 окт. 2012 г., 09:07
Баунтиjsfiddle, который вы должны исправить:jsfiddle.net/eMwKd/1
 Cade Roux26 апр. 2012 г., 20:27
Было бы лучше подумать о том, чтобы отойти от textarea и использовать вместо этого contenteditable div? Просто выбросить это туда.
 undefined25 окт. 2012 г., 23:34
Вы смотрели на решения для редактирования кода JS (я нене знаете, есть ли в них автозаполнение?
 undefined28 окт. 2012 г., 13:25
@SamSaffron: я хотел посмотреть, как они отображают всплывающее окно автозаполнения.
 Sam Saffron28 окт. 2012 г., 01:01
@xyu, да, но я боюсь, что они считаются немного тяжелыми здесь.
 enobrev28 окт. 2012 г., 10:07
Опубликовал потенциальное решение, которое работает намного лучше (комментируя, чтобы уведомить людей, которые смотрят эту ветку)
 Sam Saffron30 окт. 2012 г., 14:05
Редакторы на основе @xyu dom (codemirror / ace) могут использовать методы вставки dom для определения местоположения, это довольно просто.

Ответы на вопрос(11)

Версия 2 моего хакерского эксперимента

Эта новая версия работает с любым шрифтом, который можно настроить по запросу, и с любым размером текстовой области.

Заметив, что некоторые из вас все еще пытаются заставить это работать, я решил попробовать новый подход. Мои результаты намного лучше на этот раз - по крайней мере, на Google Chrome на Linux. У меня больше нет ПК с Windows, поэтому я могу тестировать только на chrome / firefox на Ubuntu. Мои результаты работают на 100% последовательно на Chrome, и пустьСкажем, где-то около 70 - 80% на Firefox, но я неЯ не могу представить, что было бы невероятно трудно найти несоответствия.

Эта новая версия опирается на объект Canvas. В моемпримерЯ на самом деле показываю тот самый холст - просто чтобы вы могли видеть его в действии, но это очень легко сделать с помощью скрытого объекта холста.

Это, безусловно, хак, и я заранее прошу прощения за довольно сложный код. По крайней мере, в Google Chrome он работает согласованно, независимо от того, какой шрифт я установил, или размера текстовой области. я использовалСэм ШафраньПример отображения координат курсора (серый фон). Я также добавил "Перемешать» ссылку, так что вы можете видеть, как она работает в различных размерах и стилях шрифта / texarea, и наблюдать за обновлением позиции курсора на лету. Я рекомендую посмотреть наполная страница демо так что вы можете лучше видеть, как сопутствующий холст подыгрывает.

подведу итог как это работает...

Основная идея заключается в том, что мыпытаемся перерисовать текстовую область на холсте как можно ближе. Поскольку браузер использует один и тот же механизм шрифтов для texarea, мы можем использовать canvas 'Функциональность измерения шрифта, чтобы выяснить, где вещи. Оттуда мы можем использовать доступные нам методы canvas для определения наших координат.

Прежде всего, мы настраиваем наш холст в соответствии с размерами текстовой области. Это полностью для визуальных целей, так как размер холста недействительно не имеет значения в нашем результате. С холстомЧтобы фактически обеспечить средство переноса слов, мне пришлось заклинать (красть / одалживать / разбирать вместе) средства разбиения строк, чтобы как можно лучше соответствовать текстовой области. Это где тыСкорее всего, вам понадобится сделать кросс-браузерную настройку.

После переноса слов все остальное - базовая математика. Мы разделяем строки на массив, чтобы имитировать перенос слов, и теперь мы хотим перебрать эти строки и пройти весь путь вниз до точки, где заканчивается наш текущий выбор. Для этого мыпросто подсчитываем персонажей и как только мы обойдемselection.endМы знаем, что зашли достаточно далеко. Умножьте количество строк до этой точки на высоту строки, и вы получитеy координат.

x координата очень похожа, за исключением того, что мыповторное использованиеcontext.measureText, Пока мыраспечатать правильное количество символов, которое даст нам ширину строки, которая 's обращается к Canvas, который заканчивается после последнего записанного символа, который является символом перед текущимselection.end позиция.

При попытке отладки этого для других браузеров нужно искать, где строки несломаться правильно. Вы'В некоторых местах вы увидите, что последнее слово в строке на холсте может быть перенесено на текстовую область или наоборот. Это связано с тем, как браузер обрабатывает перенос слов. До тех пор, пока вы получаете обертку на холсте, соответствующую текстовой области, ваш курсор должен быть правильным.

Вставлю источник ниже. Вы должны быть в состоянии скопировать и вставить его, но если вы это сделаете, я прошу вас загрузить свою собственную копию jquery-fieldselection вместо того, чтобы щелкнуть ее на моем сервере.

мы также вырослиновая демоверсия так же какскрипка

Удачи!



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

            // https://stackoverflow.com/a/11124580/14651
            // https://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>
    
    

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

ошибка

Там'Я помню одну ошибку. Если вы поместите курсор перед первой буквой в строке, он показывает "позиция" как последняя буква в предыдущей строке. Это связано с тем, как работает selection.end. Я неЯ думаю, что это должно быть слишком сложно, чтобы найти этот случай и исправить его соответствующим образом.

Версия 1

Оставьте это здесь, чтобы вы могли видеть прогресс без необходимости копаться в истории редактирования.

Это'не идеально, и этоОпределенно, это хак, но я получил довольно хорошую работу на WinXP IE, FF, Safari, Chrome и Opera.

Насколько я могу сказать, тамНевозможно напрямую определить х / у курсора в любом браузере.Метод IE,упомянутый отАдам Беллер это интересно, но, к сожалению, не кросс-браузер. Я подумал, что лучше всего будет использовать символы в качестве сетки.

К сожалениюs никакая информация о метрике шрифта не встроена ни в один из браузеров, что означает, что моноширинный шрифт является единственным типом шрифта, который 'будет иметь последовательное измерение. Также там'Нет надежных средств для определения ширины шрифта из высоты шрифта. Сначала яЯ пытался использовать процент от высоты, который работал отлично. Затем я изменил размер шрифта и все пошло к черту.

Я попробовал один метод, чтобы выяснить ширину символа, который должен был создать временную текстовую область и продолжать добавлять символы, пока scrollHeight (или scrollWidth) не изменился. Это кажется правдоподобным, но примерно на полпути по этой дороге я понял, что могу просто использовать атрибут cols в текстовой области, и решил, что в этом испытании достаточно хаков, чтобы добавить еще один. Это означает, что вы можетеt установить ширину текстовой области через css. Вы должны использовать cols для этого, чтобы работать.

Следующая проблема, с которой я столкнулся, заключается в том, что даже когда вы устанавливаете шрифт с помощью CSS, браузеры сообщают о шрифте по-разному. Когда ты неустановить шрифт, Mozilla используетmonospace по умолчанию IE используетCourier NewОпера"Courier New" (с кавычками), сафари,'Lucida Grand' (с одинарными кавычками). Когда вы установите шрифт наmonospace, Mozilla и т. е. взять то, что вы им даете, Safari выходит как-webkit-monospace и Опера остается с."Courier New"

Итак, теперь мы инициализируем некоторые переменные. Убедитесь, что вы также установили высоту строки в CSS. Firefox сообщает правильную высоту строки, но IE сообщал "нормальный" и я нене беспокойтесь о других браузерах. Я просто установил высоту строки в моем CSS и это разрешило разницу. У меня нетt протестировано с использованием ems вместо пикселей. Высота символа - это просто размер шрифта. Вероятно, следует заранее установить это в вашем CSS.

Кроме того, еще одна предварительная настройка, прежде чем мы начнем размещать персонажей - что заставило меня почесать голову. Для ie и mozilla символы texarea являются < cols, все остальное <= символы Таким образом, Chrome может вмещать 50 символов в поперечнике, но Mozilla и т. Д. Будет прерывать последнее слово за чертой.

Теперь мы'собирается создать массив позиций первого символа для каждой строки. Мы перебираем все символы в текстовой области. Если оно'Новая строка, мы добавляем новую позицию в наш массив строк. Если оно's пробел, мы пытаемся выяснить, если текущийслово" будет соответствовать линии, которую мыили если этособирается подтолкнуть к следующей строке. Пунктуация считается частью "слово", У меня нетT протестирован с вкладками, но естьЕсть строка для добавления 4 символов для символа табуляции.

Как только у нас есть массив позиций строк, мы перебираем и пытаемся определить, на какой строке находится курсор. Мы'Вы используете "Конец" выбора в качестве нашего курсора.

x = (позиция курсора - позиция первого символа строки курсора) * ширина символа

y = ((линия курсора + 1) * высота строки) - позиция прокрутки I '

м используюJQuery 1.2.6,JQuery-fieldselection, а такжеJQuery-размеры

Демо:http://enobrev.info/cursor/

И код:

<!--?xml version="1.0" encoding="UTF-8" ?-->


    
        <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>
    
    
        <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 sa,pien, 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>
    

 Sam Saffron29 окт. 2012 г., 07:47
Интересно, пробелы в конце строки вызывают смещение
 undefined25 окт. 2012 г., 23:37
Работает на Firefox (в основном).
 enobrev13 авг. 2013 г., 20:22
Попробую восстановить пример позже сегодня.
 Tuukka Mustonen29 апр. 2010 г., 15:41
Хорошо, но я нахожу это с ошибками в Chrome (2.0.172.37). Для воспроизведения сначала установите курсор в верхней части текстовой области, затем нажмите 2 раза, чтобы текстовая область прокручивалась и курсор перемещался вниз. После этогоА вот и я!" -label всегда располагается слишком низко (около двух строк текста). Firefox, похоже, страдает от того же самого синдрома, но он быстро фиксирует положение (div появляется в неправильном месте только один раз). Что может быть уловкой здесь?
 Parris27 окт. 2012 г., 03:51
Я ненавижу придираться, потому что это удивительно; однако, если вы сделаете текстовую область шире, вы в конечном итоге нарушите это. Я бы порекомендовал добавить событие по изменению размера. Это, однако, остается сломанным, даже когда вы продолжаете щелкать вокруг. Возможно, ширина / высота рассчитываются на основе исходной ширины / высоты элемента? (Chrome на OSX)
 iamgopal17 июл. 2010 г., 13:00
Я неНе думаю, что они многое раскрывают, но вот соответствующий пост в блоге ...googledocs.blogspot.com/2010/05/...
 enobrev28 окт. 2012 г., 10:03
Проверьте верхнюю часть этого поста для лучшей версии.
 Sam Saffron28 окт. 2012 г., 17:59
jsbin.com/egadoj/17/edit это становится все сложнее
 Dan Dascalescu17 мар. 2014 г., 13:23
Вот Это Да! Удивительное количество усилий ушло на этот ответ, ноЭто более простое решение - отразитьtextarea вdiv с тем же стилем, и создатьspan на выбор начать. Кодгораздо проще понятьи отлично работает в Chrome, Firefox, IE9.
 enobrev16 июл. 2010 г., 19:26
@iamgopal, как я могу увидеть это в действии?
 Sam Saffron28 окт. 2012 г., 16:04
Я просто думаю, что у меня работает не-холст рев, действительно как твоя твист-холстjsbin.com/egadoj/12/edit
 enobrev17 июл. 2010 г., 17:22
Я вижу, что тыповторяю По крайней мере, в тот момент, когда я открываю Google Docs и Chromium 'С инструментами разработчика, это выглядит как обычный HTML. При попытке получить всплывающее меню (например, с проверкой орфографии), оно просто помещает интервал вокруг слова, и я думаю, что ониперехватывая координаты пролета. Что касается их объяснения в этом сообщении в блоге, если ониВы явно размещаете символы один за другим (может быть, с помощью canvas?), тогданет сомнений, что этоНастроен таким образом, чтобы вся информация о местоположении была легко доступна.
 iamgopal16 июл. 2010 г., 18:52
В недавнем документе Google используется курсор, нарисованный вручную, и рассчитанная позиция текста для текстов ... Интересно, как они это делают ....
 Cobra_Fast13 авг. 2013 г., 14:59
Я только что посмотрел на скрипку версии 2, но div всплывающей подсказки, кажется, застрял на0|0 (верхний левый угол) независимо от того, нажимаю я или редактирую текст. Возможно, потому что весь материал курсора был удален изenobrev.com...Я'

Я выиграл'Не могу объяснить проблемы, связанные с этим материалом, потому что они хорошо объяснены в других постах. Просто укажу возможное решение, у него есть ошибка, ноэто отправная точка.

К счастью, на Github есть скрипт для расчета положения каретки относительно него.с контейнером, но для этого требуется jQuery.Страница GitHub здесь:JQuery-каретка-позиционно-газопоглотитель, Спасибо Бевису. Чжао.

На основании этого я реализовал следующий код:проверить это в действииздесь, в jsFiddle.net


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


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

 Dan Dascalescu17 мар. 2014 г., 13:30
БивисСценарийглючит и больше не поддерживается, Я знаю, потому что яМы проверили все восемь плагинов получения текстовых координат на GitHub. Лучший плагин, безусловно, этоcomponent.io»s textarea-caret-position, Гораздо проще, кросс-браузер и нене требуется jQuery.

связанную с этой проблемой, на русском JavaScript-сайте.

Если вы нене понимаешь по русски попробуйте перевести на Google версиюhttp://translate.google.ru/translate?js=y&пред = _t &гл = RU &то есть = UTF-8 &Макет = 1 &eotf = 1 &и = HTTP: //javascript.ru/forum/events/7771-poluchit-koordinaty-kursora-v-tekstovom-pole-v-pikselyakh.html&сл = RU &TL = еп

Есть некоторые проблемы разметки в примерах кода в переведенной версии, так что вы можетечитайте код в оригинальном русском посте.

Идея проста. Не существует простого, универсального и кросс-браузерного метода для получения позиции курсора в пикселях. Честно говоря, есть, но только для Internet Explorer.

В других браузерах, если вам действительно нужно рассчитать его, вы должны ...

создать невидимый DIVскопировать все стили и содержимое текстового поля в этот DIVзатем вставьте HTML-элемент в ту же самую позицию в тексте, где курсор находится в текстовом полеполучить координаты этого HTML-элемента
 Dan Dascalescu17 мар. 2014 г., 13:24
Тот'Это общий алгоритм, но в нём есть разные нюансы.совместимость браузера,Component.io Команда собрала простойкросс-браузерный плагин, который работает вокруг всех крайних случаев, и нене требуется jQuery.

http://jsfiddle.net/eMwKd/4/

Единственным недостатком является то, что уже предусмотрена функцияgetCaret() разрешается в неправильной позиции при нажатии клавиши вниз. поэтому красный курсор кажется позади реального курсора, если вы не отпустите клавишу.

Я еще раз посмотрю на это.

обновление: хм, перенос слов не точен, если строки слишком длинные.

 lrsjng28 окт. 2012 г., 02:18
хм, все еще не идеально ..
 Sam Saffron28 окт. 2012 г., 02:28
все ближеjsbin.com/egadoj/4
 lrsjng28 окт. 2012 г., 02:36
тот'Это очень хорошо, вам нужно только проверить, находитесь ли вы в конце строки (в этом случае просмотр вперед не удастся)
 lrsjng28 окт. 2012 г., 02:38
Кстати, я также проверилgetCaret() функции, и кажется невозможным заставить его работать на клавиатуре. Случаи, которые выигралиЭто исправимо, когда вы перемещаетесь курсором вверх или вниз.
 Dan Dascalescu17 мар. 2014 г., 13:36
Проблема не из легких, но команда Component.io собралаплагин textarea-caret-position тот'в значительной степени идеально (без зависимостей, кросс-браузер, всего 80 строк кода, ручки полосы прокрутки, перенос, любая комбинация шрифтов и т. д.)
 Sam Saffron28 окт. 2012 г., 01:00
это самое близкое, что я могу получить:jsbin.com/egadoj/1/edit волос от того, чтобы быть решенным

шивого курсора на основе этого span 'смещения? Я обновил вашу скрипкуВот, Также здесь'только бит JS

// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
var map = [];
var pan = '<span>|</span>'

//found @ http://davidwalsh.name/detect-scrollbar-width

function getScrollbarWidth() {
    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 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;
}


$(function() {
    var span = $('#pos span');
    var textarea = $('textarea');

    var note = $('#note');

    css = getComputedStyle(document.getElementById('textarea'));
    try {
        for (i in css) note.css(css[i]) && (css[i] != 'width' && css[i] != 'height') && note.css(css[i], css.getPropertyValue(css[i]));
    } catch (e) {}

    note.css('max-width', '300px');
    document.getElementById('note').style.visibility = 'hidden';
    var height = note.height();
    var fakeCursor, hidePrompt;

    textarea.on('keyup click', function(e) {
        if (document.getElementById('textarea').scrollHeight > 100) {
            note.css('max-width', 300 - getScrollbarWidth());
        }

        var pos = getCaret(textarea[0]);

        note.text(textarea.val().substring(0, pos));
        $(pan).appendTo(note);
        span.text(pos);

        if (hidePrompt) {
            hidePrompt.remove();
        }
        if (fakeCursor) {
            fakeCursor.remove();
        }

        fakeCursor = $("<div style="width:5px;height:30px;background-color: #777;position: absolute;z-index:10000"> </div>");

        fakeCursor.css('opacity', 0.5);
        fakeCursor.css('left', $('#note span').offset().left + 'px');
        fakeCursor.css('top', textarea.offset().top + note.height() - (30 + textarea.scrollTop()) + 'px');

        hidePrompt = fakeCursor.clone();
        hidePrompt.css({
            'width': '2px',
            'background-color': 'white',
            'z-index': '1000',
            'opacity': '1'
        });

        hidePrompt.appendTo(textarea.parent());
        fakeCursor.appendTo(textarea.parent());



        return true;
    });
});

 ОБНОВИТЬ: Я вижу, что тамОшибка, если первая строка не содержит жестких разрывов строк, но если это так, похоже, она работает хорошо.

 Dan Dascalescu17 мар. 2014 г., 13:47
Мне понравилось немного о поиске ширины полосы прокрутки. Там'это общееошибка, однако, что большинство библиотек каретных координат имеют,component.io плагин textarea-caret-position адресует это, плюс это неt требует jQuery, работает в Chrome, FF и IE и занимает всего 80 строк кода.

блог кажется, слишком близко, чтобы ответить на вопрос. У меня нетЯ не пробовал сам, но, по словам автора, его тестировали с FF3, Chrome, IE, Opera, Safari. Код включенGitHub

 Sam Saffron30 окт. 2012 г., 14:21
Проект на github - это хорошее усилие, но не пуленепробиваемое @enobrev имеет гораздо более жесткую реализацию. В частности, код на github не внедряет слово-перенос с переносом слов, он не выполняет замены вставки пробелов должным образом (несколько пробелов отключают его).
 Dan Dascalescu17 мар. 2014 г., 13:33
Я недавно получил автора этого плагинаосудить это в пользу textarea-caret-position. @SamSaffron: я нене виделлюбые репозитории такого рода на enobrev 'с GitHub?
 Mahyar30 окт. 2012 г., 13:47
тот'именно то, что вам нужно, он используетprototype.js рамки, и это делает работу. код написан длящелчок событие, так что вы можете добавитьOnKeyUp событие к нему, и там вы идете. Я также проверил это на IE, Chrome, Firefox, Safari, Opera, и это работает абсолютно. Надеюсь, вы найдете это полезным.

это вас порадует, оно покажет позицию выделения и положение курсора, поэтому попробуйте проверить таймер, чтобы получить автоматическую позицию, или снимите отметку, чтобы получить позицию, нажав на кнопку «Выбрать выделение».

   <form>
 <p>
 <input type="button" onclick="evalOnce();" value="Get Selection">
timer:
<input id="eval_switch" type="checkbox" onclick="evalSwitchClicked(this)">
<input id="eval_time" type="text" value="200" size="6">
ms
</p>
<textarea id="code" cols="50" rows="20">01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 Sample text area. Please select above text. </textarea>
<textarea id="out" cols="50" rows="20"></textarea>
</form>
<div id="test"></div>
<script>

function Selection(textareaElement) {
this.element = textareaElement;
}
Selection.prototype.create = function() {
if (document.selection != null && this.element.selectionStart == null) {
return this._ieGetSelection();
} else {
return this._mozillaGetSelection();
}
}
Selection.prototype._mozillaGetSelection = function() {
return {
start: this.element.selectionStart,
end: this.element.selectionEnd
 };
 }
Selection.prototype._ieGetSelection = function() {
this.element.focus();
var range = document.selection.createRange();
var bookmark = range.getBookmark();
var contents = this.element.value;
var originalContents = contents;
var marker = this._createSelectionMarker();
while(contents.indexOf(marker) != -1) {
marker = this._createSelectionMarker();
 }
var parent = range.parentElement();
if (parent == null || parent.type != "textarea") {
return { start: 0, end: 0 };
}
range.text = marker + range.text + marker;
contents = this.element.value;
var result = {};
result.start = contents.indexOf(marker);
contents = contents.replace(marker, "");
result.end = contents.indexOf(marker);
this.element.value = originalContents;
range.moveToBookmark(bookmark);
range.select();
return result;
}
Selection.prototype._createSelectionMarker = function() {
return "##SELECTION_MARKER_" + Math.random() + "##";
}

var timer;
var buffer = "";
function evalSwitchClicked(e) {
if (e.checked) {
evalStart();
} else {
evalStop();
}
}
function evalStart() {
var o = document.getElementById("eval_time");
timer = setTimeout(timerHandler, o.value);
}
function evalStop() {
clearTimeout(timer);
}
function timerHandler() {
clearTimeout(timer);
var sw = document.getElementById("eval_switch");
if (sw.checked) {
evalOnce();
evalStart();
}
}
function evalOnce() {
try {
var selection = new Selection(document.getElementById("code"));
var s = selection.create();
var result = s.start + ":" + s.end;
buffer += result;
flush();
 } catch (ex) {
buffer = ex;
flush();
}
}
function getCode() {
// var s.create()
// return document.getElementById("code").value;
}
function clear() {
var out = document.getElementById("out");
out.value = "";
}
function print(str) {
buffer += str + "\n";
}
function flush() {
var out = document.getElementById("out");
out.value = buffer;
buffer = "";
 } 
</script>


look the demo here : jsbin.com

 Sam Saffron30 окт. 2012 г., 14:04
это просто расположение курсора, довольно просто для решения кросс-браузер и очень хорошо задокументировано

textarea но это точно работает дляdiv с .contenteditable

Вы можете использоватьRange API. Вот так: (да, вам действительно нужны только эти 3 строки кода)

// get active selection
var selection = window.getSelection();
// get the range (you might want to check selection.rangeCount
// to see if it's popuplated)
var range = selection.getRangeAt(0);

// will give you top, left, width, height
console.log(range.getBoundingClientRect());

я не уверен в совместимости браузера, но ямы обнаружили, что он работает в последних версиях Chrome, Firefox и даже IE7 (кажется, я тестировал 7, в противном случае это было 9).

Вы даже можете сделатьпсих' такие вещи: если выповторно печатать"#hash" и курсор находится на последнемhВы можете посмотреть в текущем диапазоне для# персонаж, переместить диапазон обратно наn персонажи и получитьограничивающего Rect из этого диапазона, это заставит popup-div казатьсяпридерживаться' к слову.

Один небольшой недостаток в том, чтоcontenteditable может быть немного глючит иногда. Курсор любит ходить в невозможные места, и теперь вам приходится иметь дело с вводом HTML. Но я'Я уверен, что поставщики браузеров будут решать эти проблемы, поскольку больше сайтов начинают их использовать.

Еще один совет, который я могу дать: посмотрите наrangy библиотека. Он пытается быть полнофункциональной кросс-совместимой библиотекой диапазонов. Ты ненеобходимость это, но если выРабота со старыми браузерами может стоить того.

это Кажется, что сообщение в блоге посвящено вашему вопросу, но, к сожалению, автор признает, что проверял его только в IE 6.

DOM в IE не предоставляет информацию относительно относительного положения в терминах символов; однако он предоставляет ограничивающие и смещенные значения для элементов управления, отображаемых браузером. Таким образом, я использовал эти значения для определения относительных границ персонажа. Затем, используя JavaScript TextRange, я создал механизм для работы с такими мерами, чтобы вычислить позицию строки и столбца для шрифтов фиксированной ширины в пределах данной TextArea.

Во-первых, относительные границы для TextArea должны быть рассчитаны на основе размера используемого шрифта фиксированной ширины. Для этого исходное значение TextArea должно быть сохранено в локальной переменной JavaScript и очищено значение. Затем создается TextRange, чтобы определить верхнюю и левую границы TextArea.

что этот вопрос дублирует вопрос, заданный месяцем ранее, и ямы ответилиВот, Я'Я оставлю ответ только по этой ссылке, так как этот вопрос должен был быть закрыт как дубликат лет назад.

Копия ответа

я искал плагин для текстовых координат каретки дляМетеор-автозаполнение, так что я'Мы оценили все 8 плагинов на GitHub. Победитель, безусловно,TextArea-каретка-позиции отСоставная часть.

Характеристикиточность пикселейникаких зависимостей вообщесовместимость браузера: Chrome, Safari, Firefox (несмотря надва ошибки у него есть), IE9 +; может работать, но не проверено в Opera, IE8 или старшеподдерживает любое семейство и размер шрифта, а также преобразование текстаобласть текста может иметь произвольные отступы или границыНе перепутайте горизонтальные или вертикальные полосы прокрутки в текстовой областиподдерживает жесткие возвраты, табуляции (кроме IE) и последовательные пробелы в текстеправильная позиция в строках длиннее столбцов в текстовой областинет "призрак» положение в пустом пространстве в конце строки при переносе длинных словВот'демо -http://jsfiddle.net/dandv/aFPA7/

Как это устроено

Зеркало

 создается вне экрана и в стиле точно так же, как</code>, Затем текст текстовой области до каретки копируется в div и<code><span></code> вставляется сразу после него. Затем текстовое содержимое span устанавливается на оставшуюся часть текста в текстовой области, чтобы точно воспроизвести перенос в поддельном div.</p><p>Это единственный метод, который гарантированно обрабатывает все крайние случаи, относящиеся к переносу длинных строк. Это's также используется GitHub для определения положения его @<strong></strong> выпадающий пользователь.</p>
 daliusd03 янв. 2019 г., 08:35
скопируйте эту строку "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii» и наслаждайтесь результатом. Это полностью терпит неудачу в Firefox, и есть проблема и в Chrome.

Координаты каретки Textarea X / Y - плагин jQuery

Также будет лучше использовать элемент div сcontenteditable атрибут, если вы можете использовать функции HTML5.

Ваш ответ на вопрос