获取插入符号在文本区域中的偏移位置(以像素为单位) [英] Get the offset position of the caret in a textarea in pixels

查看:161
本文介绍了获取插入符号在文本区域中的偏移位置(以像素为单位)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的项目中,我试图以 textarea 为单位获得插入符号的偏移位置。可以这样做吗?

In my project I'm trying to get the offset position of the caret in a textarea in pixels. Can this be done?

在问这里之前,我已经浏览了很多链接,特别是Tim Down,但我找不到适用于IE8 +,Chrome和Firefox的解决方案。似乎 Tim Down正在研究这个问题

Before asking here, I have gone through many links, especially Tim Down's, but I couldn't find a solution which works in IE8+, Chrome and Firefox. It seems Tim Down is working on this.

一些其他 链接,我发现有许多问题,如没有找到插入位置的顶部偏移。

Some other links which I have found have many issues like not finding the top offset of the caret position.

我试图获得偏移位置插入符号,因为我想在 textarea 中显示一个自动完成的建议框,方法是根据插入符的偏移位置进行定位。

I am trying to get the offset position of the caret because I want to show an auto-complete suggestion box inside the textarea by positioning it based on the offset position of the caret.

PS:我不能使用 contenteditable div 因为我写了很多与<$ c相关的代码$ c> textarea 。

PS: I can't use a contenteditable div because I have written lots of code related to a textarea.

推荐答案

这是一种使用 rangyinputs 瘦长 jQuery

它基本上复制了整个文本从 textarea 内到相同大小的 div 。我设置了一些CSS,以确保在每个浏览器中, textarea div 将它们的内容完全相同办法。

It basically copies the whole text from inside the textarea into a div of the same size. I have set some CSS to ensure that in every browser, the textarea and the div wrap their content in exactly the same way.

当点击 textarea 时,我会读出插入符所在的字符索引,然后插入一个插入 span 位于 div 内的相同索引处。通过这样做,我最终遇到插入符号 span 的问题,如果用户点击一行的开头,则跳回到上一行。为了解决这个问题,我检查前一个字符是否是空格(这将允许换行),如果是 true ,我将它包装在 span 中,然后在 span 。现在我比较这两个 span 之间的最高值,如果它们不同,有一些包装正在进行,所以我假设 top #nextword span 值c $ c>相当于插入位置。

When the textarea is clicked, I read out at which character index the caret is positioned, then I insert a caret span at the same index inside the div. By only doing that I ended up having an issue with the caret span jumping back to the previous line if the user clicked at the start of a line. To fix that I check if the previous character is a space (which would allow a wrap to occur), if that is true, I wrap it in a span, and I wrap the next word (the one directly after the caret position) in a span. Now I compare the top values between these two span's, if they differ, there was some wrapping going on, so I assume that the top and the left value of the #nextword span are equivalent to the caret position.

这种方法仍然可以改进,我敢肯定我没有想到可能出错的一切,即使我有,但是我没有费心为所有人实施修复,因为我现在没有时间这样做,你需要注意的一些事情:

This approach can still be improved upon, I'm sure I haven't thought of everything that could possibly go wrong, and even if I have, then I haven't bothered implementing a fix for all of them as I don't have the time to do so at the moment, a number of things that you would need to look at:


  • 它还没有处理用 Enter (已修复)插入的硬回车

  • 连续输入多个空格时的定位中断(已修复)

  • 我认为连字符也允许内容包装发生..

  • it doesn't yet handle hard returns inserted with Enter (fixed)
  • positioning breaks when entering multiple spaces in a row (fixed)
  • I think hyphens would allow a content wrap to occur as well..

目前它在Windows 8上使用最新版本的浏览器的工作方式完全相同Chrome,Firefox,IE和Safari。我的测试不是很严格。

Currently it works exactly the same way across browsers here on Windows 8 with the latest versions of Chrome, Firefox, IE and Safari. My testing has not been very rigorous though.

这是一个 jsFiddle

我希望它能帮助你,至少它可能会给你你有一些想法可以建立。

I hope it will help you, at the very least it might give you some ideas to build on.


  • 我为你找到了一个 ul ,它位于正确的位置,修复了一个Firefox问题,其中 textarea 选择在DOM操作之后,没有重新回到原来的位置。

  • I have included a ul for you which is positioned in the right spot, and fixed a Firefox issue where the textarea selection was not re-set back to its original spot after the DOM manipulations.

我添加了IE7 - IE9支持并修复了评论中指出的多字选择问题。

I have added IE7 - IE9 support and fixed the multiple word selection issue pointed out in the comments.

我添加了对使用 Enter 插入的硬回车以及连续多个空格的支持。

I have added support for hard returns inserted with Enter and multiple spaces in a row.

我有修复了 ctrl + shift + 左箭头文本选择方法的默认行为问题。

I have fixed an issue with the default behaviour for the ctrl+shift+left arrow text selection method.

JavaScript

function getTextAreaXandY() {

    // Don't do anything if key pressed is left arrow
    if (e.which == 37) return;     

    // Save selection start
    var selection = $(this).getSelection();
    var index = selection.start;

    // Copy text to div
    $(this).blur();
    $("div").text($(this).val());

    // Get current character
    $(this).setSelection(index, index + 1);
    currentcharacter = $(this).getSelection().text;

    // Get previous character
    $(this).setSelection(index - 1, index)
    previouscharacter = $(this).getSelection().text;

    var start, endchar;
    var end = 0;
    var range = rangy.createRange();

    // If current or previous character is a space or a line break, find the next word and wrap it in a span
    var linebreak = previouscharacter.match(/(\r\n|\n|\r)/gm) == undefined ? false : true;

    if (previouscharacter == ' ' || currentcharacter == ' ' || linebreak) {
        i = index + 1; // Start at the end of the current space        
        while (endchar != ' ' && end < $(this).val().length) {
            i++;
            $(this).setSelection(i, i + 1)
            var sel = $(this).getSelection();
            endchar = sel.text;
            end = sel.start;
        }

        range.setStart($("div")[0].childNodes[0], index);
        range.setEnd($("div")[0].childNodes[0], end);
        var nextword = range.toHtml();
        range.deleteContents();
        var position = $("<span id='nextword'>" + nextword + "</span>")[0];
        range.insertNode(position);
        var nextwordtop = $("#nextword").position().top;
    }

    // Insert `#caret` at the position of the caret
    range.setStart($("div")[0].childNodes[0], index);
    var caret = $("<span id='caret'></span>")[0];
    range.insertNode(caret);
    var carettop = $("#caret").position().top;

    // If preceding character is a space, wrap it in a span
    if (previouscharacter == ' ') {
        range.setStart($("div")[0].childNodes[0], index - 1);
        range.setEnd($("div")[0].childNodes[0], index);
        var prevchar = $("<span id='prevchar'></span>")[0];
        range.insertNode(prevchar);
        var prevchartop = $("#prevchar").position().top;
    }

    // Set textarea selection back to selection start
    $(this).focus();
    $(this).setSelection(index, selection.end);

    // If the top value of the previous character span is not equal to the top value of the next word,
    // there must have been some wrapping going on, the previous character was a space, so the wrapping
    // would have occured after this space, its safe to assume that the left and top value of `#nextword`
    // indicate the caret position
    if (prevchartop != undefined && prevchartop != nextwordtop) {
        $("label").text('X: ' + $("#nextword").position().left + 'px, Y: ' + $("#nextword").position().top);
        $('ul').css('left', ($("#nextword").position().left) + 'px');
        $('ul').css('top', ($("#nextword").position().top + 13) + 'px');
    }
    // if not, then there was no wrapping, we can take the left and the top value from `#caret`    
    else {
        $("label").text('X: ' + $("#caret").position().left + 'px, Y: ' + $("#caret").position().top);
        $('ul').css('left', ($("#caret").position().left) + 'px');
        $('ul').css('top', ($("#caret").position().top + 14) + 'px');
    }

    $('ul').css('display', 'block');
}

$("textarea").click(getTextAreaXandY);
$("textarea").keyup(getTextAreaXandY);

HTML

<div></div>
<textarea>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</textarea>
<label></label>
<ul>
    <li>Why don't you type this..</li>
</ul>

CSS

body {
    font-family: Verdana;
    font-size: 12px;
    line-height: 14px;
}
textarea, div {
    font-family: Verdana;
    font-size: 12px;
    line-height: 14px;
    width: 300px;
    display: block;
    overflow: hidden;
    border: 1px solid black;
    padding: 0;
    margin: 0;
    resize: none;
    min-height: 300px;
    position: absolute;
    -moz-box-sizing: border-box;
    white-space: pre-wrap;
}
span {
    display: inline-block;
    height: 14px;
    position: relative;
}
span#caret {
    display: inline;
}
label {
    display: block;
    margin-left: 320px;
}
ul {
    padding: 0px;
    margin: 9px;
    position: absolute;
    z-index: 999;
    border: 1px solid #000;
    background-color: #FFF;
    list-style-type:none;
    display: none;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
    span {
        white-space: pre-wrap;
    }
}
div {
    /* Firefox wrapping fix */
    -moz-padding-end: 1.5px;
    -moz-padding-start: 1.5px;
    /* IE8/IE9 wrapping fix */
    padding-right: 5px\0/;
    width: 295px\0/;
}
span#caret
{
    display: inline-block\0/;
}

这篇关于获取插入符号在文本区域中的偏移位置(以像素为单位)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆