插入符号在文本区域的第一行吗?在最后一行?

Posted

技术标签:

【中文标题】插入符号在文本区域的第一行吗?在最后一行?【英文标题】:Is the caret on the first line of the textarea? On the last line? 【发布时间】:2014-08-27 14:55:36 【问题描述】:

给定一个不固定宽度字体的文本区域,我想知道插入符号(由element.selectionEnd 给出)是在文本的第一行还是在最后一行。

为了避免错误的答案,这里有一些不起作用的解决方案:

\n 上拆分:一个句子可以分成两行,因为它对于文本区域的宽度来说太长了。 测量插入符号之前的文本(例如,通过将文本复制到具有相同样式的 div 并测量跨度的高度):插入符号 之后 的某些字符可能会更改换行点(通常词之间)。

这是一个用于测试并消除一些歧义的小提琴(是的,它是一个 textarea 元素,它不仅仅是一行等):http://jsbin.com/qifezupu/4/edit

注意事项:

我不喜欢 Internet Explorer 9 或移动浏览器。 我需要一个可靠的解决方案,适用于插入符号的所有位置。 有很多棘手的细节使大多数想法在实践中无法使用,请在回答之前先建立一个可行的小提琴。

【问题讨论】:

我认为纯 textarea 是不可能的(在我看来)。但是如果你可以使用一些解析纯 html 的编辑器,那么你可以定义哪一行是最后一行,哪一行是第一行。 【参考方案1】:

目前最好的解决方案:

创建一个具有两个跨度的临时 div,以及用于模仿(字体、自动换行、滚动条)文本区域的换行行为的 CSS 样式。 用插入符号前的文本填充第一个跨度。 用插入符号后面的文本填充第二个跨度。 使用跨度的 offset.top 和元素高度来了解我们是在第一行还是在最后一行。

我可以看到它工作吗?

我做了一个小提琴,让它的工作原理一目了然:http://jsbin.com/qifezupu/31/edit

如何使用:

我做了一个 jQuery 插件:https://github.com/Canop/taliner.js

这里是相关的演示页面:http://dystroy.org/demos/taliner/demo.html

代码:

$.fn.taliner = function()

    var $t = this,
        $d = $('<div>').appendTo('body'),
        $s1 = $('<span>').text('a').appendTo($d),
        $s2 = $('<span>').appendTo($d),
        lh =  $s1.height();

    $d.css(
        width: $t.width(),
        height: $t.height(),
        font: $t.css('font'),
        fontFamily: $t.css('fontFamily'), // for FF/linux
        fontSize: $t.css('fontSize'),
        whiteSpace : 'pre-wrap',
        wordWrap : 'break-word',
        overflowY: 'auto',
        position: 'fixed',
        bottom: 0,
        left: 0,
        padding: 0,
        zIndex: 666
    );

    var lh = $s1.height(),
        input = this[0],
        se = input.selectionEnd,
        v = input.value,
        res = ;

    $s1.text(v);
    res.linesNumber = $s1.height()/lh|0;

    $s1.text(v.slice(0, se));
    $s2.text(v.slice(se));
    res.caretOnFirstLine = input.selectionEnd===0
        || ($s1.height()<=lh && $s1.offset().top===$s2.offset().top);
    res.caretOnLastLine = $s2.height()===lh;

    $d.remove();
    return res;

真的有用吗?

还是有问题。我们在 javascript 中获得的关于插入符号位置的唯一信息是 element.selectionEnd。这很糟糕。

以下是两行文本区域上可能的插入符号位置:

| A | B | C |
| D | E |

如您所见,插入符号的位置比字符间的位置要多。更具体地说,您在 C 和 D 之间有两个不同的插入符号位置,但 DOM 不提供任何方式来区分它们。

这意味着有时,如果您单击行尾的右侧,库将无法判断它是行尾还是下一行的开头。

【讨论】:

感谢您分享您的解决方案。我试图实现相同的功能,但用于在多个 contenteditable 元素之间导航,挂钩到上/下键盘事件(这似乎比选项卡更自然,这很有效)。我想知道 Rangy / Tim Down 是否可以帮助解决这个问题。【参考方案2】:

有几种方法可以获取文本区域内的插入符号位置(以像素为单位):Offset possition of the caret in a textarea in pixels

小心,他们可能在使用不同的浏览器时遇到问题。 您可以设置一个预定义的行高或字体大小,以便稍后用于获取插入符号的位置。

我没有测试任何方法,但我们假设用于获取插入符号位置(以像素为单位)的函数返回相对于插入符号底部的 y 组件。 您指定的行高为 15 像素。 假设方法返回 (20, 45)。当前行 = y/15 = 45/15 = 3

JS Fiddle 来演示如何根据 Dan 的解决方案来完成:Current line from caret coordinates

我添加了一个显示当前行的跨度。可悲的是,我在 Chrome 上的测试表明这不是 100% 准确的:当你用鼠标选择第三行时,它说你在第四行。

Line:
<span id="line">0</span>

update 函数的末尾(在 HTML 部分)我添加了以下行:

//24 is the top padding of the textarea
//16 is the line-height
line.innerText = (coordinates.top - 24) / 16;

【讨论】:

这看起来是迄今为止最好的解决方案 - 边缘情况下较小的浏览器错误似乎不会破坏相关行为。感谢您找到这个!【参考方案3】:

基于@Flater 所说的关于添加文本宽度的另一种解决方案。这个想法(总而言之)是:

    获取文本区域的宽度 获取光标位置之前的文本并计算其宽度 获取光标位置之后的文本并计算其宽度 如果文本前>文本区域宽度或不包含换行符(以防第一行文本较少),光标在第一个行 如果文本后>文本区域宽度或不包含换行符,则光标不在最后一行

代码:

$('#t').keyup(function(e)
  first_line="true";
  last_line="true";
  text = $(this).val();
  width = $(this).width();
  var cursorPosition = $(this).prop("selectionStart");
  txtBeforeCaret = text.substring(0, cursorPosition);
  txtAfterCaret = text.substring(cursorPosition);
  widthBefore = $('#holder').text(txtBeforeCaret).width();
  widthAfter = $('#holder').text(txtAfterCaret).width();
  match1 = txtBeforeCaret.match(/\n/);
  if(txtAfterCaret!==null)match2=txtAfterCaret.match(/\n/g);
  if(widthBefore>width || match1)first_line="false";
  if(widthAfter>width || (match2!==null && match2.length)) last_line="false";
  $('#f').html(first_line);
  $('#l').html(last_line);
);

#holder 是一个 div 创建并隐藏(用于查找文本宽度)。

DEMO

【讨论】:

我没有时间看看它们是否容易修复,但有两个问题:1)如果你把插入符号放在第二个开头,它不会给出正确的答案第 2 行)它在第一行末尾不起作用 this text 另一个问题:如果您在 textarea 中键入一些 html,它会中断(在我的解决方案中,我通过使用 text 和一些 css 设置解决了这个问题)。 @dystroy 在一些错误之后我稍微纠正了它。测试它并告诉我。然而,调整大小并不完美(调整大小时,您需要输入正确的输出而不是移动光标)。 there are still problems(在 chromium/linux 上测试) @dystroy 没错,问题似乎是无法检测到换行符(由于文本框较小而自动生成)。我无法使用这种方法。【参考方案4】:

您可以通过将一段文本放入一个 div 并获取该 div 的宽度来测量它的长度。

简而言之,看看这个jsFiddle。每次更改文本框中的文本时,都会提醒您文本宽度。

所以我建议做以下事情:

查找 textarea 内容的第一行和最后一行。接下来的步骤适用于任一行。 要查看一行是否因为 textarea 的宽度而被打断,请使用我建议的 sn-p 检查文本宽度是否大于 textarea 的宽度。 如果它更长,您可以迭代地开始检查该行的子字符串。如果您找到一个与您的 textarea 的宽度相匹配的内容,您可以很确定这就是换行符所在的位置。

我不确定您要执行什么操作,但您可以使用此方法估算插入符号的位置。 它可能不是像素完美的,因为 div 的宽度与文本的宽度略有不同。

这是测量文本宽度的 sn-p:

$(document).ready(function() 

    $("#txtbox").keyup(function() 
        var the_Text = $("#txtbox").val();

        var width = $("#testdiv").text(the_Text).width();

        alert("The text width is : " + width + " px");
    );

);

$("#testdiv") 是页面上的隐藏 div。没什么花哨的,唯一使用的 CSS 是确保用户看不到它。

【讨论】:

如果您认为这应该可行,您可以尝试使您的解决方案适应the fiddle I gave 吗?请注意,我不关心像素位置,只关心插入符号的 exact 行位置。【参考方案5】:

这是一个使用这个 jQuery 插件 (https://github.com/Codecademy/textarea-helper) 来获取插入符号位置的小提琴(y 位置是你需要的):http://jsbin.com/qifezupu/11/edit

很遗憾,您将看到需要进行一些调整。这只是一个想法。

【讨论】:

以上是关于插入符号在文本区域的第一行吗?在最后一行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在文本区域的当前插入符号位置插入文本

选择文本区域的最后一行

如何防止文本区域中的重复空格

如何通过在文本区域中的当前行末尾按空格来转到下一行

删除 JavaFx 文本区域的第一行

在文本区域中获取插入符号 XY 坐标 [重复]