如何突出显示最接近鼠标的文本行?

8

我有一段很长的文本,并想为用户提供阅读帮助:当前行应该被突出显示。为了简化,我只使用鼠标的Y坐标(这样,鼠标指针不会妨碍)。我有一个ID为content的大DIV,填满整个宽度,一个小DIV,类名为content,用于文本(在此处查看示例)。

我正在使用jQuery 1.4。如何突出显示最接近当前鼠标位置的文本行?


遍历DOM,使用offsetwidth+parent.offsetWidth获取元素的绝对位置等。 - Warty
更新了我的答案,包括一个可能的解决方案。 - Peter Bailey
@Tom -- 除了你链接的那个问题比这个新两年,所以从技术上讲,另一个问题是这个问题的重复。 - LittleBobbyTables - Au Revoir
@Tom:除了下面的答案(使用 getClientRects())适用于具有不同行高的文本。 - Aaron Digulla
现在这两个问题都是彼此的副本。这很奇妙... - Aaron Digulla
3个回答

18

我不确定jQuery是否能够帮助到你,但是你可以查看在MSDNMDC上记录的element.getClientRects方法。更具体地说,MSDN上的this example有点类似于你想要实现的效果,它使用了一个巧妙的z-index元素来突出显示行,该元素位于getClientRects()返回的坐标下的文本后面。

通过循环遍历文档中onmousemove返回的TextRectangle对象,并检查鼠标光标的y值是否大于每个矩形的顶部并且小于底部,然后将巧妙的z-index div移动到相同的位置/高度,您应该可以实现相同的效果。

所有主流浏览器都支持getClientRects()


http://jsbin.com/avuku/15

已更新 - 可在Chrome、IE6/7/8、Firefox、Opera和Safari中工作。在其他浏览器中遇到的初始问题与DIV需要设置为display: inline有关。
再次更新 - 我不得不参考这个答案来回答一些新问题,所以我花时间更新了它以在窗口调整大小时重新计算行数。看起来其他人也在尝试,现在已经是第15版。


1
哦,现在跨浏览器工作了吗?那我想这肯定是最好的选择。 - Pekka
我认为你只需要为窗口添加一个 onresize 处理程序来重新填充 lines 变量,这样就会非常稳定。 - Peter Bailey
@Aaron:确实。我提供的代码旨在证明概念,虽然这个理论足以吸引我去构建一个样例,但我只希望有更多时间来尝试它。也许我应该减少在 Stack Overflow 上的时间,多写些代码;-) - Andy E
1
这就是为什么 Stack Overflow 是如此棒的原因。 - Sam
@Alex:我写的例子只是一个概念证明,没有理由不能适应滚动偏移。 - Andy E
显示剩余3条评论

7

我不认为你能在没有明确换行的情况下实现这一点(即,换行符或<br>元素)。

据我所知,DOM无法发现特定文本块在字符级别或像素级别上的换行位置 - 包括我所知道的Range API - 更不用说文本可以采取的动态形式,例如浏览器的文本缩放功能。

但是,如果你能以某种方式生成/注入明确的行结尾,那么我认为我有一个解决方案供你使用。

编辑

由于Pekka答案中提供的精彩信息,我已经组合出一个功能原型,但它有一个重要的限制 - 仅适用于纯文本内容。元素主体中存在的任何HTML都将被剥离。

jQuery.fn.wrapLines = function( openTag, closeTag )
  {
    var dummy = this.clone().css({
            top: -9999,
            left: -9999,
            position: 'absolute',
            width: this.width()
        }).appendTo(this.parent())
      , text = dummy.text().match(/\S+\s+/g);

    var words = text.length
      , lastTopOffset = 0
      , lines = []
      , lineText = ''
    ;

    for ( var i = 0; i < words; ++i )
    {
      dummy.html(
          text.slice(0,i).join('') +
          text[i].replace(/(\S)/, '$1<span/>') +
          text.slice(i+1).join('')
      );

      var topOffset = jQuery( 'span', dummy ).offset().top;

      if ( topOffset !== lastTopOffset && i != 0 )
      {
        lines.push( lineText );
        lineText = text[i];
      } else {
        lineText += text[i];
      }

      lastTopOffset = topOffset;
    }
    lines.push( lineText );

    this.html( openTag + lines.join( closeTag + openTag ) + closeTag );
  };

  $(function()
  {
    $('p').wrapLines( '<span class="line">', '</span>' );
  });
span.line {
  display: inline;
}
span.line:hover {
  background-color: lightblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p style="max-width:400px">
 one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty twenty-one twenty-two twenty-three
</p>


+1 太棒了,运行得非常顺畅!现在有 两个 可用的示例。 - Pekka
去除HTML标签似乎是一个很大的限制。不过,对于其巧妙性还是要点赞的。 - user241244
做得太棒了!!这正是我一直在寻找的。 - Jinu Kurian

3
我想到的最好方法是将每一行分割成一个带有“highlight”设置的<span><div>元素,该元素具有:hover CSS类。
span.line:hover { background-color: lightblue; }

那将是最便宜的解决方案,因为浏览器会自己处理所有的高亮显示。如果您想要花哨的效果,仍然可以通过为每一行添加mouseovermouseout事件来实现。
当然,困难的部分是在浏览器的换行处将内容拆分成行。您需要动态地进行这样做,以便行实际上反映了浏览器断开文本的位置。
也许这个问题的接受答案是朝着正确方向迈出的一步: 使用jQuery获取特定行 引用:
它通过整个元素(实际上是元素的克隆)插入每个单词中的一个元素。缓存span的顶部偏移量——当此偏移量更改时,我们可以假设我们正在进入新行。

不错的发现,以及在另一个问题中寻找任意文本行的好创意方法。 - Peter Bailey
我同意,这是一种创造性的方法,但如果有1000个单词,它不会变得非常缓慢吗?在这种情况下,您预计DOM插入将非常昂贵。 - Andy E
@Andy 在我看来,这种方法的最大优点是它在页面加载时使用了JavaScript,但在实际高亮显示时不会使用,而实时高亮显示的解决方案将一直进行计算。此外,我认为即使在较慢的机器上,DOM插入也是可以接受的 - 但这当然取决于所使用的方法。必须实际组合这些内容并进行一些分析才能确定。 - Pekka
看看我使用 getClientRects() 的示例答案 - 这样更简洁,而且我认为计算并不昂贵,因为返回的集合仅包含具有静态属性的对象。 - Andy E

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接