【问题标题】:Determine the position index of a character within an HTML element when clicked确定单击时 HTML 元素中字符的位置索引
【发布时间】:2011-11-12 16:34:48
【问题描述】:

我有一个 HTML 元素,里面只有可见的文本。此示例是 <div> 元素,但它可以是 <span><p> 或其他 DOM 元素。

<div>This is a simple example.</div>

单击时,我可以获得光标在 div 表面上的位置,但我需要在单击时确定最近字符的位置和/或其在 div.innerHTML 字符串中的索引。

我在 SVG 文本实体 here 的“getCharNumAtPosition”方法中发现了类似的实现。

是否有可能在 JavaScript 中实现这样一个与 HTML 一起工作的函数?

(如果解决方案可以在大多数现代浏览器中移植,可以与大多数书面语言一起使用,并且基于相对稳定的标准,这样以后就不会出现问题,那么解决方案将是最有用的。)

【问题讨论】:

    标签: javascript position character


    【解决方案1】:
    $('div').click( function () {
      getSelectionPosition (); 
    });
    
    
    function getSelectionPosition () {
      var selection = window.getSelection();
      console.log(selection.focusNode.data[selection.focusOffset]);
      alert(selection.focusOffset);
    }
    

    这适用于“点击”以及大多数浏览器的“范围”。 (selection.type = "caret"/selection.type = "range")。

    selection.focusOffset() 为您提供在内部节点中的位置。如果元素是嵌套的,例如在&lt;b&gt;&lt;span&gt; 标记中,它将为您提供内部元素内的位置,而不是全文,或多或少。我无法使用focusOffset 和“插入符号”类型“选择”子标签的第一个字母(单击,而不是范围选择)。当您单击第一个字母时,它会给出标签开始前最后一个元素的位置加 1。当您单击第二个字母时,它会正确地给出“1”。但是我没有找到访问子元素的第一个元素(偏移量 0)的方法。这种“选择/范围”的东西似乎有问题(或者对我来说非常不直观)。 ^^

    但它在没有嵌套元素的情况下使用起来非常简单! (适用于您的 &lt;div&gt;

    Here is a fiddle

    重要编辑 2015-01-18:

    这个答案在被接受后可以恢复,但现在不再有效,原因如下。其他答案现在最有用。

    • 马修的一般回答
    • Douglas Daseeco 提供的工作示例。

    Firefox 和 Chrome 都调试了 window.getSelection() 行为。可悲的是,它现在对这个用例没有用。 (阅读文档,IE 9 及更高版本的行为应相同)。

    现在,字符的中间用于决定偏移量。这意味着单击一个字符可以返回 2 个结果。第一个字符为 0 或 1,第二个字符为 1 或 2,依此类推。

    我更新了JSFiddle example

    请注意,如果您调整窗口大小(Ctrl + 鼠标),Chrome 上的某些点击行为会出现很多错误。

    【讨论】:

    • 非常感谢,这是我能得到的最佳答案)
    • 离题,但是为什么一个命名函数包裹在一个匿名函数中,而不仅仅是$('element').event(function())部分中的命名函数,在示例中是$('div').click( getSelectionPosition() );?我知道我的是一个通用的 JS 语法问题,但是这个特定的代码示例向我提出了这个问题。解释赞赏!
    • 你是对的 $('div').click(getSelectionPosition); 在这种情况下可以完美工作(但不是 $('div').click(getSelectionPosition()); !)。我习惯性地使用了间接形式。我只能邀请您使用 google javascript 回调。
    • 处理低于 DOM 对象级别的字符不太可能非常便携。
    • @micnic,既然这个答案不能跨常用浏览器移植(其作者在上面的 2015 年确定),也许你可以选择一个有效的答案,这样人们就不会使用 buggy认为它可以被证明的代码?
    【解决方案2】:

    您可以使用 JavaScript 将每个字符分解为它自己的 SPAN 标记,并为每个字符添加一个 onclick 事件。然后,在单击 SPAN 时从事件数据中读取 x、y 位置。

    同样,您将可以访问使用 event.target.innerHTML 点击的字符

    【讨论】:

    • 我正在考虑这个想法,但我想在一个使用 HTML 元素的简单记事本 Web 应用程序中实现这个功能,如果我为每个字符使用跨度,它会占用大量内存,我想要找到一个低级的解决方案,如果它存在:)
    • 我会说你可以根据字体大小来计算字符的位置,但除非你使用的是固定宽度的字体,否则这是行不通的。
    • 字体大小不行,可以ctrl+鼠标滚轮。我想 op 最好的希望在于 window.getSelection() 和范围对象(和 createRange)
    • 即使在固定宽度的字体中,您也无法预测每种情况下的换行。在某些情况下,使用 getSelection 可能会出错,具体取决于字符位置的处理方式,并且存在可移植性问题。过去过多的内存使用可能不再是问题。整个问题都围绕着每个字符不是 DOM 对象的想法。纯粹的 OO 解决方案是让它们如此,这并不是特别新颖。它一直在完成。我想知道真实浏览器中 div 或 span DOM 节点的字节开销是多少?
    • @Douglas Daseeco 我绝对解决了 如何 做到这一点 - 实施取决于您。这很简单......
    【解决方案3】:

    此示例可快速加载中等大小的文本块,可移植且稳健。虽然它的优雅并不立即显现出来,但可靠性在于在国际字符和 DOM 事件侦听器之间创建简单的一对一对应1

    最好只依赖广泛采用的字符编码、DOM 和 ECMA 标准,如本例所示。其他方法通常依赖于像 getSelection() 这样的低级事件目标属性或函数,它们既不稳定,也不能跨浏览器和语言移植2

    可以针对特定应用以多种方式修改示例代码。例如,可以使用其他机制来选择为 charClick 回调初始化哪些 DOM 对象。 i 循环中也可以支持代理对。

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Character Click Demo</title>
          <script type='text/javascript'>
              var pre = "<div onclick='charClick(this, ";
              var inf = ")'>";
              var suf = "</div>"; 
              function charClick(el, i) {
                  var p = el.parentNode.id;
                  var s = "para '" + p + "' idx " + i + " click";
                  ele = document.getElementById('result');
                  ele.innerHTML = s; }
              function initCharClick(ids) {
                  var el; var from; var length; var to; var cc;
                  var idArray = ids.split(" ");
                  var idQty = idArray.length;
                  for (var j = 0; j < idQty; ++ j) {
                      el = document.getElementById(idArray[j]);
                      from = unescape(el.innerHTML);
                      length = from.length;
                      to = "";
                      for (var i = 0; i < length; ++ i) {
                          to = to + pre + i + inf + from.charAt(i) + suf;
                      el.innerHTML = to; } }
          </script>
          <style>
              .characters div {
                  padding: 0;
                  margin: 0;
                  display: inline }
          </style>
      </head>
      <body class='characters' onload='initCharClick("h1 p0 p2")'>
          <h1 id='h1'>Character Click Demo</h1>
          <p id='p0'>Test &#xE6;&#x20AC; &ndash; &#xFD7;&#xD8; &mdash; string.</p>
          <p id='p1'>Next ? ?E para.</p>
          <p id='p2'>&copy; 2017</p>
          <hr>
          <p id='result'>&nbsp;</p>
      </body>
      </html>
    

    建议在国际化生产场景中使用它之前进行全面测试。如果发现错误,请提出修改建议或添加 cmets,以便添加改进。


    备注

    [1] 这些事件侦听器可以直接添加,但需要为浏览器的可移植性或额外的库进行分支,而这些库没有为此用例增加特别的速度或便利价值。

    [2] 请参阅W3C's UI events page 中有关选择事件的说明。

    【讨论】:

    • 与我们在后台内容编辑器中使用的 JQuery 组件相比,此代码在浏览器和移动设备上的运行效果更好。
    【解决方案4】:

    在许多情况下,可能的解决方案是在标签内容的每个字符之间添加零宽度空格字符(U+200B)。然后使用 javascript window.getSelection().focusOffset + 一些简单的计算就可以了。

    示例 html 代码(包括零宽度空格字符):

    <div onclick='magic(this)'>T​h​i​s​ ​i​s​ ​a​ ​s​i​m​p​l​e​ ​e​x​a​m​p​l​e​.</div>
    

    示例 javascript 代码:

    <script>
        function magic(e)
        {
            var s, p, c;
            s = e.innerHTML;
            p = window.getSelection().focusOffset;
            c = s.charAt((p >> 1) << 1);
            alert("index:" + (p >> 1) + " char: " + c);
        }
    </script>
    

    似乎可以广泛使用,甚至在 Internet Explorer 上!

    jsFidle:http://jsfiddle.net/by1a9pfm/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      相关资源
      最近更新 更多