3

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

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

肯定有更容易的事情吗?

4

4 回答 4

8

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

于 2012-08-02T04:26:10.577 回答
2

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

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

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

试试看。

于 2012-08-01T04:01:44.410 回答
1

这是我为实现迈克尔在他的回答中所写的内容所做的努力:

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
}
于 2019-03-30T09:47:22.483 回答
0

Placing each character in a document model object is not as obnoxious as it sounds. HTML parsing, DOM representation, and event handling is quite efficient in terms of memory and processing in modern browsers. A similar mechanism is used at a low level to render the characters too. To simulate what the browser does at that level takes much work.

  • Most documents are constructed with variable width characters
  • Wrapping can be justified or aligned in a number of ways
  • There is not a one to one mapping between characters and bytes
  • To be a truly internationalized and robust solution, surrogate pairs must be supported too 1

This example is lightweight, loads quickly, and is portable across common browsers. Its elegance is not immediately apparent, much reliability is gained by establishing a one to one correspondence between international characters and event listeners.

  <!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] This simple example does not have handling for surrogate pairs, but such could be added in the body of the i loop.

于 2017-02-08T04:39:08.650 回答