如何确定单击的textnode中的字符?

时间:2012-08-01 03:46:04

标签: javascript html dom

我可以设置一个事件监听器来告诉我何时在HTML文档中的某个位置发生鼠标点击。但是如果在某些文本上发生了点击,我需要知道文本中的哪个字符发生了点击。有没有办法做到这一点?

我可以想到一些非常令人讨厌的解决方案。例如,对于文档中的每个单个字符,我可以将其包装在具有自己的事件的单独元素中。或者,因为我可以告诉发生了哪个文本节点,我可以使用clientWidth执行某种计算(基本上就像模拟文本的渲染),以确定点击发生在哪个字符。

肯定会有更容易的事情吗?

4 个答案:

答案 0 :(得分:8)

捕获鼠标事件后,将元素中的文本拆分为两个单独的跨度。查看每个跨度的偏移量以确定事件发生的位置。现在将该跨度拆分为两个并再次进行比较。重复,直到您的跨度具有单个字符,其坐标包含鼠标单击的坐标。由于这实际上是二元搜索,因此整个过程相当快,并且与替代方案相比,跨度总数较低。找到角色后,可以解散跨距,并将所有文本返回到原始元素。

答案 1 :(得分:2)

不幸的是,你必须将每个字符包装在一个元素中,但是你不必为每个字符附加一个事件监听器。当对元素触发click事件时,它会冒泡到其父元素。然后,您可以使用事件的target属性检索实际单击的元素。

假设我们在名为textElement的元素中有一些文字。它包含每个字符span。如果我们想要点击字符来删除它们,我们可以使用以下代码:

textElement.addEventListener('click', function(e) {
    textElement.removeChild(e.target);
}, false);

<强> Try it out.

答案 2 :(得分:0)

将每个角色放置在文档模型对象中并不像听起来那样令人讨厌。 HTML解析,DOM表示和事件处理在现代浏览器中的内存和处理方面非常有效。在较低级别使用类似的机制来渲染字符。模拟浏览器在该级别执行的操作需要做很多工作。

  • 大多数文档都是使用可变宽度字符构建的
  • 包装可以通过多种方式证明或对齐
  • 字符和字节之间没有一对一的映射
  • 要成为真正国际化和强大的解决方案,还必须支持代理对 1

此示例轻量级,加载速度快,可在常见浏览器中移植。它的优雅并不是立竿见影的,通过在国际角色和事件听众之间建立一对一的对应关系,可以获得很大的可靠性。

  <!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) {
                      cc = from.charAt(i);
                      to = to + pre + i + inf + cc + 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'>&#xE6;&#x20AC; &ndash; &#xFD7;&#xD8; &mdash;</p>
      <p id='p1'>Next  E para.</p>
      <p id='p2'>&copy; 2017</p>
      <hr>
      <p id='result'>&nbsp;</p>
  </body>
  </html>

[1]这个简单的例子没有代理对的处理,但是可以在i循环的主体中添加。

答案 3 :(得分:0)

这是我努力实现迈克尔在他的answer中所写的内容:

function hitCharBinSearch(mClientX, inmostHitEl) {

    const originalInmost = inmostHitEl
    const bareText = inmostHitEl.firstChild.textContent
    var textNode = inmostHitEl.firstChild
    var textLenghtBeforeHit = 0
    do {
        let textNodeR = textNode.splitText(textNode.length / 2)
        let textNodeL = textNode

        let spanL = document.createElement('span')
        spanL.appendChild(textNodeL)
        let spanR = document.createElement('span')
        spanR.appendChild(textNodeR)

        inmostHitEl.appendChild(spanL)
        inmostHitEl.appendChild(spanR)

        if (mClientX >= spanR.getBoundingClientRect().left) {
            textNode = textNodeR
            inmostHitEl = spanR
            textLenghtBeforeHit += textNodeL.length
        }
        else {
            textNode = textNodeL
            inmostHitEl = spanL
        }
    } while (textNode.length > 1)

    /* This is for proper caret simulation. Can be omitted */
    var rect = inmostHitEl.getBoundingClientRect()
    if (mClientX >= (rect.left + rect.width / 2)) 
        textLenghtBeforeHit++
    /*******************************************************/

    originalInmost.innerHTML = bareText
    return textLenghtBeforeHit
}