我能把多行文本中的每一行都包裹在一个span标签中吗?

15

我一直在尝试弄清楚如何实现这个功能(如果可能的话),但是一直没有头绪...

我有一些文本会换行到多行。我想要检测每个单独的行,并将其包装在一个标签中。最后,我想从循环数组中为每个分配一个类。

例如...!

<div id="quote">
    I have some text that
    wraps onto three lines
    in this container
</div>

我想让jQuery解析这些行,检测它们包裹的位置,并将其转换为如下:

<div id="quote">
    <span class="red-bg">I have some text that</span>
    <span class="orange-bg">wraps onto three lines</span>
    <span class="yellow-bg">in this container</span>
</div>

我想要动态地实现这个功能是因为我正在使用响应式模板,所以有时同样的文本只会换行成两行,或者在iPhone上可能会换成四行。

这可行吗?我找到了这个-> http://vidasp.net/tinydemos/numberOfLines.html 它可以计算一段文本中使用的行数,但它并不能做到我需要的功能。


这个 div 块是动态生成的还是在加载时创建的? - kobe
@gov div块本身是内联的,我只想在加载时进一步样式化它。 - BellamyStudio
2
这个插件应该可以工作 - http://pmbennett.net/demos/jquery-finding-lines-of-text.html (检查页面源代码中的js文件)。 - user1104010
4个回答

12

看起来你正在询问如何在浏览器自然换行的位置拆分文本。很不幸,这并不是一件简单的事情。这也不是稳健的 - 考虑以下场景:

  • 用户浏览到您的页面,渲染 div 并触发 onload 事件,
  • 3 个 span 元素从文本节点创建,每个换行文本对应一个 span 元素,
  • 用户调整浏览器大小,div 的大小发生变化。

结果是,span 元素不再与文本行的起始和结束位置一致。当然,这种情况可以通过使用固定宽度元素或在浏览器重新调整大小时重新调整整个内容来避免,但这只是一个破解示例。

尽管如此,这并不容易。 曾经出现过一个类似的问题(虽然目标不同),并提供了两个解决方案,这两个解决方案都可能对你有所帮助:

Solution 1: getClientRects()

不要实际将文本包装在 span 元素中,而是使用getClientRects()获取每行文本的位置和尺寸。 然后,创建所需数量的 span 元素并将它们定位/调整大小到每行文本的背面。

优点

  • 快速;getClientRects 返回每行的位置
  • 简单;代码比解决方案 2 更优雅

缺点

  • 换行文本必须由内联元素包含。
  • 实际上不会应用任何样式(如 font-weight 或 font-color)到文本。只对像 background-color 或 border 这样的东西有用。

提供的演示展示了如何高亮鼠标当前所在行的文本。

解决方案2:拆分、合并、循环、合并

使用split()方法将文本按单词边界或空格划分为数组。使用<span></span>包裹每个元素,用</span><span>连接它们并将整个字符串重新组成一个字符串,并用<span></span>包装起来。将包含元素中的原始文本节点替换为结果HTML。现在,迭代每个元素并检查它在容器内的y位置。当y位置增加时,您知道已经到达新行,之前的元素可以合并成一个单独的元素。

优点

  • 每一行都可以应用任何CSS属性,例如字体加粗或文本修饰。
  • 每行可以有自己的事件处理程序。

缺点

  • 由于许多DOM和字符串操作,速度慢且难以控制。

结论

可能有其他方法可以实现您的目标,但我不确定自己有什么。 TextNode.splitText(n)在传递要拆分的字符的数字索引时可以将TextNode一分为二。上述两种解决方案都不完美,并且只要包含元素调整大小,它们就会中断。


1
非常感谢您提供如此精彩、全面的回复。getClientRects()解决方案看起来非常有趣(很抱歉我没有自己找到它)-我将调查这条路线,因为后者虽然非常聪明,但也更加繁重-我可能会用第一个解决方案实现我的目标。 - BellamyStudio

7
我制作了一个演示,实现了Andy E(上面的)的第二种解决方案。即分割、连接、循环、合并。
以下是算法:
var spanInserted = $('#someText').html().split(" ").join(" </span><span>");
var wrapped = ("<span>").concat(spanInserted, "</span>");
$('#someText').html(wrapped);
var refPos = $('#someText span:first-child').position().top;
var newPos;
$('#someText span').each(function(index) {
    newPos = $(this).position().top   
    if (index == 0){
       return;
    }
    if (newPos == refPos){
        $(this).prepend($(this).prev().text() + " ");
        $(this).prev().remove();
    } 
    refPos = newPos;
});

享受...


非常好。这也为我创建了一堆空的跨度,我在循环中将其删除了。 - MastaBaba

1
var classes = ",red-bg,orange-bg,yellow-bg".split(",")
var txt = $('#quote').html().split("\n")
//this gives you FIVE items because of the leading and trailing CRs
//so we skip the first and last item in the loop
var output = ""
for(var x=1;x<txt.length-1;x++) {
    output = output + "<span class='"+classes[x]+"'>"+txt[x]+"</span>"
}
$('#quote').html(output)

这非常接近!但它检测到代码中的换行符,而不是在呈现的页面上。因此,如果我在文本中放置一个换行符,span 将正确换行,但在页面上,颜色会在一行中间改变,因为浏览器正确地忽略了新行。 - BellamyStudio
我想,与其使用内联元素,我可以使用块级元素,但它们仍然依赖于源代码中存在换行符,而这是我无法保证的,因为我可能会对其进行GZip压缩... - BellamyStudio

0
这将获取文本节点,但我不确定它是否有所帮助。
$("#quote")
  .contents()
  .filter(function() {
    return this.nodeType == 3;
  })

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