基于线的截断方法

2025-06-04

基于线的截断方法

我经常收到要求将我构建的网站上的内容摘录截断为一定行数的请求。虽然这听起来很简单,但实际操作起来却很困难,主要有两个原因:首先,由于我们构建的网站需要响应式缩放以适应各种设备尺寸,因此我们不能假设内容框在任何给定的屏幕尺寸下宽度都相同;其次,除非我们使用等宽字体,否则任何文本占用的行数都取决于内容本身,因为大多数字体的字符宽度都不同。

让我们以下面的代码片段为例:

<article>
    <h3>This is a really long heading that we’re going to need to truncate for the sake of the layout since it goes on for several lines.</h3>
    <div class=“excerpt”>
        <p>Cras dolor orci, mollis eget mi vel, faucibus malesuada velit. Phasellus egestas nec nisi non bibendum. Vestibulum faucibus mauris ac ipsum viverra volutpat. Sed lobortis justo sapien, eget ornare orci convallis ut. Nullam pulvinar, urna at faucibus dignissim, orci elit varius quam, non sodales lacus elit a nunc. Morbi eleifend velit quis tellus tempus, sed vehicula neque vulputate. Vestibulum sit amet tempus nibh, sit amet semper mi. Integer sit amet quam eget ligula luctus pulvinar at non ligula. Suspendisse a fringilla lorem, cursus sodales odio. Aliquam ac odio eget nulla consectetur dictum eu quis massa. Sed volutpat ante non felis condimentum vestibulum. In tempor tristique varius. Nunc est nunc, tincidunt quis metus et, semper molestie eros. <a href=“#” class=“readmore”>Read More</a>
    </div>
</article>
Enter fullscreen mode Exit fullscreen mode

我们可以采取哪些方法?

基于 CSS 的剪辑

一个非常简单的解决方案是使用 CSS 为文本所在的容器设置最大高度。如果我们知道行高,我们可以将其乘以想要显示的行数,从而得到框应该达到的高度,以便正确裁剪文本。

h3 {
    line-height: 26px;
    height: 52px;
    overflow: hidden;
}

.content {
    line-height: 24px;
    height: 100%;
    overflow: hidden;
    max-height: 72px;
}

.content p {
    margin: 0;
}
Enter fullscreen mode Exit fullscreen mode

此解决方案无需 JavaScript,因此性能非常出色。您还可以添加少量 JavaScript,通过将 .content 容器的最大高度设置为远高于其内容的高度(例如 9999999px)来显示隐藏内容,这样也更利于过渡动画。

但是,如果您需要在末尾包含“更多”或“继续”链接,或者想要添加省略号来指示文本已被截断,那么您将需要一些更强大的功能,因为此解决方案将隐藏恰好超过指定行数的段的末尾。

优点:性能变化最小,无需修改标记
缺点:无法在文本末尾使用阅读更多链接或省略号,特定于某些 CSS 选择器

基于 JavaScript 的剪辑

通过使用 Javascript(在本例中是 jQuery,尽管我相信您可以不用它来编写)来操作 HTML 文档,我们可以获得更灵活的结果。

在这种情况下,如果我们知道元素的行高,并且它保持不变,我们就可以分离出任何“阅读更多”链接,用空格分隔文本,然后遍历每个单词,直到我们发现内容现在比我们想要容纳它的框高。我们还应该将原始文本存储在一个属性中,以便在容器大小发生变化时更新摘录。

$(window).on(resize load, function() {
    $(.content p).each(function() {
        var lineHeight = 16; // content's line-height
        var lines = 3; // number of lines to show
        // only truncate if the text is longer than the desired size; if not, skip it
        if ($(this).height() > lineHeight * lines) {
            if ($(this).find('a.readmore').length > 0) {
                    $(this).attr('data-link', $(this).find('a.readmore')); // if there's a readmore link, separate it out and put it on a data attribute
                    $(this).find('a.readmore').detach(); // remove the link from the HTML
            }
            if ($(this).attr('title')) {
                    $(this).text($(this).attr('title'));
            }
            $(this).attr('title', $(this).text().trim()); // set the text as the title attribute to preserve it
            $(this).text(""); // empty the content
            var str = "";
            var prevstr = "";
            var words = text.split(" "); // split text into words
            var link = this.attr('data-link');
            for (var i = 0; i < words.length; i++) {
                if (this.height() > lines * lineHeight) {
                    // if we've spilled over onto the next line, roll it back a word and break the loop
                    this.html(prevstr.trim() + "&hellip; " + (typeof link !== 'undefined' ? ' ' + link : ''));
                    break;
                }
                prevstr = str;
                // update the working string with the next word
                str += words[i] + " ";
                // update the content in the document
                this.html(str.trim() + "&hellip;" + (typeof link !== 'undefined' ? ' ' + link : ''));
            }
            // if the last iteration caused us to spill over a line, roll back one word
            if (this.height() > lines * lineHeight) {
                this.html(prevstr.trim() + "&hellip; " + (typeof link !== 'undefined' ? ' ' + link : ''));
            }
        }
    });
});
Enter fullscreen mode Exit fullscreen mode

优点:可以维护更多链接和省略号
缺点:特定于某些 CSS 选择器

JavaScript 对任意项进行截断

上述解决方案非常灵活,但需要指定行高。如果我们想将该解决方案应用于任意元素,或者如果该特定元素在某些 CSS 断点处指定了不同的行高,该怎么办?

对于许多属性,人们可以直接获取属性,无论是从 vanilla Javascript 还是通过使用 $(elt).css(“line-height”) 方法,但许多浏览器返回的行高值略有不同,而且,我们无法保证行高将采用哪种单位。

我希望能有一个真正简单易行、人人都能DIY的绝妙解决方案,但我实在太累了,就下载了line-height模块,把它放在我自己的脚本前面。(不出所料,line-height 模块也是为了补充基于行的截断解决方案而编写的。)

利用它,我们可以将赋值给 lineHeight 变量的静态数字替换为window.lineHeight(this[0]);,这样就能以易于使用的方式返回行高值。现在,它很容易转换成一个自定义函数,我们可以用元素和给定的行数来调用它,甚至可以将其转换成一个 jQuery 插件,用作任何元素的方法。

优点:保留更多链接和省略号,可以轻松重复使用
缺点:使用外部库来设置行高

演示!

以下是 Codepen 演示中整合的全部内容,所有内容都包装在 jQuery 插件中:

文章来源:https://dev.to/carlymho/line-based-truncation-methods-51bk
PREV
如何改善工程组织内部(及其他领域)的知识共享
NEXT
我使用 Chakra-Ui 至今学到的东西 结论