在固定宽度和高度内追加省略号的跨浏览器多行文本溢出

我为这个问题做了一个图像,使它更容易理解。

有没有可能在 <div>上创建一个具有固定宽度和多行的省略号?

text-overflow

我到处尝试了一些 jQuery 插件,但是找不到我要找的那个。有什么建议吗? 有什么想法吗?

116796 次浏览

不确定这是否是您要找的,它使用 min-height 而不是 height。

    <div id="content" style="min-height:10px;width:190px;background:lightblue;">
<?php
function truncate($text,$numb) {
// source: www.kigoobe.com, please keep this if you are using the function
$text = html_entity_decode($text, ENT_QUOTES);
if (strlen($text) > $numb) {
$text = substr($text, 0, $numb);
$etc = "...";
$text = $text.$etc;
}
$text = htmlentities($text, ENT_QUOTES);
return $text;
}
echo truncate("this is a multi-lines text block, some lines inside the div, while some outside", 63);
?>
</div>

你可能做不到(目前?)没有像 Courier 这样的固定宽度字体。使用固定宽度的字体,每个字母占据相同的水平空间,因此您可以计算字母数量,并将结果与当前的 ems 或 exs 字体大小相乘。然后你只需要测试一行有多少个字母,然后把它们分开。

或者,对于非固定字体,您可以为所有可能的字符(如 i = 2px,m = 5px)创建一个映射,然后进行计算。不过有很多丑陋的工作。

扩展@DanMan 的解决方案: 在使用可变宽度字体的情况下,可以使用平均字体宽度。这有两个问题: 1)有太多 W 的文本会溢出; 2)有太多 I 的文本会被提前截断。

或者你可以采取最坏的情况,使用字母“ W”的宽度(我认为它是最宽的)。这消除了上面的问题1,但加剧了问题2。

一种不同的方法可以是: 将 overflow: clip保留在 div 中,并使用 float: right; position: relative; bottom: 0px;(未测试)添加一个省略号部分(可能是另一个 div 或图像)。技巧是使图像出现在文本末尾的上方。

您也只能在知道图像将溢出时(比如,在大约100个字符之后)才能显示图像

只是一个简单的基本想法。

我使用以下标记进行测试:

<div id="fos">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nisi ligula, dapibus a volutpat sit amet, mattis et dui. Nunc porttitor accumsan orci id luctus. Phasellus ipsum metus, tincidunt non rhoncus id, dictum a lectus. Nam sed ipsum a lacus sodales eleifend. Vestibulum lorem felis, rhoncus elementum vestibulum eget, dictum ut velit. Nullam venenatis, elit in suscipit imperdiet, orci purus posuere mauris, quis adipiscing ipsum urna ac quam.</p>
</div>

还有 CSS:

#fos { width: 300px; height: 190px; overflow: hidden; }
#fos p { padding: 10px; margin: 0; }

应用这个 jQuery 将实现所期望的结果:

var $p = $('#fos p');
var divh = $('#fos').height();
while ($p.outerHeight() > divh) {
$p.text(function (index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}

它反复尝试删除文本的最后一个单词,直到达到所需的大小。由于溢出: 隐藏; 进程仍然是不可见的,即使关闭了 JS,结果仍然是“视觉上正确的”(当然没有“ ...”)。

如果在服务器端结合使用一个合理的截断(这只会留下一点开销) ,那么它将运行得更快:)。

同样,这不是一个完整的解决方案,只是一个想法。

更新: 增加了一个 小提琴演示

试试 JQuery. dotdotdot插件。

$(".ellipsis").dotdotdot();

使用这段代码,如果元素的高度受 max-height 样式的限制,那么就不需要额外的包装器 div。

// Shorten texts in overflowed paragraphs to emulate Operas text-overflow: -o-ellipsis-lastline
$('.ellipsis-lastline').each(function(i, e) {
var $e = $(e), original_content = $e.text();
while (e.scrollHeight > e.clientHeight)
$e.text($e.text().replace(/\W*\w+\W*$/, '…'));
$e.attr('data-original-content', original_content);
});

此外,它将原始文本保存在一个只能使用样式显示的数据属性中,例如在鼠标上:

.ellipsis-lastline {
max-height: 5em;
}
.ellipsis-lastline:before {
content: attr(data-original-content);
position: absolute;
display: none;
}
.ellipsis-lastline:hover:before {
display: block;
}

非常简单的函数就可以了。

指令:

  $scope.truncateAlbumName = function (name) {
if (name.length > 36) {
return name.slice(0, 34) + "..";
} else {
return name;
}
};

观看内容:

<#p>\{\{truncateAlbumName(album.name)}}<#/p>

纯 JS 解决方案基于 ba mega-apa 的解决方案,并对试图给出高度/max-height 小于元素 line 的人进行了一些清理。高度:

  var truncationEl = document.getElementById('truncation-test');
function calculateTruncation(el) {
var text;
while(el.clientHeight < el.scrollHeight) {
text = el.innerHTML.trim();
if(text.split(' ').length <= 1) {
break;
}
el.innerHTML = text.replace(/\W*\s(\S)*$/, '...');
}
}


calculateTruncation(truncationEl);

我有一个很好的解决方案,但是省略号使用了渐变。这样做的好处是不需要进行任何 JavaScript 计算,而且它适用于包括表单元格在内的可变宽度容器。它使用了一些额外的 div,但是实现起来非常简单。

Http://salzerdesign.com/blog/?p=453

编辑: 对不起,我不知道链接是不够的。解决方案是在文本周围放置一个 div,并设置 div 的样式以控制溢出。在 div 内部放置另一个带有“渐变”渐变的 div,这个渐变可以通过使用 CSS 或图像(对于旧的 IE)来实现。渐变从透明到表单元格的背景颜色,比省略号稍宽。如果文本很长并且溢出,那么它将位于“淡出”div 下并且看起来“淡出”。如果文本很短,淡入是不可见的,所以没有问题。通过将容器的高度设置为文本行高度的倍数,可以对这两个容器进行调整,以便显示一行或多行。“淡出”div 可以定位为只覆盖最后一行。

这里有一个纯 CSS 的方法来实现这一点: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/

以下是一个总结:

enter image description here

<html>
<head>
<style>
html, body, p { margin: 0; padding: 0; font-family: sans-serif;}


.ellipsis {
overflow: hidden;
height: 200px;
line-height: 25px;
margin: 20px;
border: 5px solid #AAA; }


.ellipsis:before {
content:"";
float: left;
width: 5px; height: 200px; }


.ellipsis > *:first-child {
float: right;
width: 100%;
margin-left: -5px; }


.ellipsis:after {
content: "\02026";


box-sizing: content-box;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;


float: right; position: relative;
top: -25px; left: 100%;
width: 3em; margin-left: -3em;
padding-right: 5px;


text-align: right;


background: -webkit-gradient(linear, left top, right top,
from(rgba(255, 255, 255, 0)), to(white), color-stop(50%, white));
background: -moz-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: -o-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: -ms-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white); }
</style>
</head>
<body>
<div class="ellipsis">
<div>
<p>Call me Ishmael.....</p>
</div>
</div>
</body>
</html>

这个问题的答案并不确切,但是我在尝试做类似的事情时偶然发现了这个页面,但是我想添加一个“查看更多”的链接,而不仅仅是一个简单的省略号。这是一个 jQuery 函数,它将为溢出容器的文本添加一个“ more”链接。就个人而言,我使用这与 Bootstrap,但当然它将工作没有。

Example more screenshot

要使用,请将文本放入一个容器中,如下所示:

<div class="more-less">
<div class="more-block">
<p>The long text goes in here</p>
</div>
</div>

当添加以下 jQuery 函数时,任何大于 adjustheight 值的 div 都将被截断,并添加一个“ More”链接。

$(function(){
var adjustheight = 60;
var moreText = '+ More';
var lessText = '- Less';
$(".more-less .more-block").each(function(){
if ($(this).height() > adjustheight){
$(this).css('height', adjustheight).css('overflow', 'hidden');
$(this).parent(".more-less").append
('<a style="cursor:pointer" class="adjust">' + moreText + '</a>');
}
});
$(".adjust").click(function() {
if ($(this).prev().css('overflow') == 'hidden')
{
$(this).prev().css('height', 'auto').css('overflow', 'visible');
$(this).text(lessText);
}
else {
$(this).prev().css('height', adjustheight).css('overflow', 'hidden');
$(this).text(moreText);
}
});
});

基于此,但更新: http://shakenandstirredweb.com/240/jquery-moreless-text

上面提到的 JQuery 插件很适合角度:

(function (angular) {
angular.module('app')
.directive('appEllipsis', [
"$log", "$timeout", function ($log, $timeout) {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {


// let the angular data binding run first
$timeout(function() {
element.dotdotdot({
watch: "window"
});
});
}
}


}
]);
})(window.angular);

相应的加价将是:

<p app-ellipsis>\{\{ selectedItem.Description }}</p>

这里有一个你可以在紧要关头使用的普通 JavaScript 解决方案:

// @param 1 = element containing text to truncate
// @param 2 = the maximum number of lines to show
function limitLines(el, nLines) {
var nHeight,
el2 = el.cloneNode(true);
// Create clone to determine line height
el2.style.position = 'absolute';
el2.style.top = '0';
el2.style.width = '10%';
el2.style.overflow = 'hidden';
el2.style.visibility = 'hidden';
el2.style.whiteSpace = 'nowrap';
el.parentNode.appendChild(el2);
nHeight = (el2.clientHeight+2)*nLines; // Add 2 pixels of slack
// Clean up
el.parentNode.removeChild(el2);
el2 = null;
// Truncate until desired nLines reached
if (el.clientHeight > nHeight) {
var i = 0,
imax = nLines * 35;
while (el.clientHeight > nHeight) {
el.innerHTML = el.textContent.slice(0, -2) + '&hellip;';
++i;
// Prevent infinite loop in "print" media query caused by
// Bootstrap 3 CSS: a[href]:after { content:" (" attr(href) ")"; }
if (i===imax) break;
}
}
}


limitLines(document.getElementById('target'), 7);
#test {
width: 320px;
font-size: 18px;
}
<div id="test">
<p>Paragraph 1</p>
<p id="target">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Paragraph 3</p>
</div>

您可以在下面的 codepen 中使用它。尝试更改 CSS 面板中的字体大小,并在 HTML 面板中进行小的编辑(例如,在某处添加额外的空间)以更新结果。不管字体大小如何,中间的段落应该总是被截断为第二个参数中传递给 limit Lines ()的行数。

Codepen: < a href = “ http://Codepen.io/thdoan/pen/BoXbEK”rel = “ nofollow”> http://Codepen.io/thdoan/pen/boxbek

用于“线夹”的 Javascript 库

请注意,“线夹紧”也被称为“多线块省略”或“垂直省略”。


Github.com/besite/jquery.dotdotdot


Github.com/josephschmitt/clamp.js


这里还有一些我没有调查过的:


线夹紧的 CSS 解决方案

有一些 CSS 解决方案,但最简单的使用 浏览器支持较差的 -webkit-line-clamp

许多人付出了巨大的努力,以使这种情况发生只使用 CSS。查看相关文章和问题:


我的建议是

简单点。除非你有大量的时间专注于这个特性,否则请选择最简单和经过测试的解决方案: 简单的 CSS 或者经过良好测试的 javascript 库。

选择一些花哨的/复杂的/高度定制的东西,你将为此付出代价。


做别人做的事

像 Airbnb 那样淡出可能是一个不错的解决方案。它可能是基本的 CSS 和基本的 jQuery。实际上,它看起来和 CSSTricks 上的这个解决方案非常相似

AirBnb "read more" solution

哦,如果你寻找设计灵感:

纯 JS 演示(没有 jQuery 和‘ while’循环)

当我寻找多行省略问题的解决方案时,我惊讶地发现没有一个好的解决方案是没有 jQuery 的。也有一些解决方案的基础上’当’循环,但我认为他们不是有效的和危险的,由于可能进入无限循环。所以我写了这段代码:

function ellipsizeTextBox(el) {
if (el.scrollHeight <= el.offsetHeight) {
return;
}


let wordArray = el.innerHTML.split(' ');
const wordsLength = wordArray.length;
let activeWord;
let activePhrase;
let isEllipsed = false;


for (let i = 0; i < wordsLength; i++) {
if (el.scrollHeight > el.offsetHeight) {
activeWord = wordArray.pop();
el.innerHTML = activePhrase = wordArray.join(' ');
} else {
break;
}
}


let charsArray = activeWord.split('');
const charsLength = charsArray.length;


for (let i = 0; i < charsLength; i++) {
if (el.scrollHeight > el.offsetHeight) {
charsArray.pop();
el.innerHTML = activePhrase + ' ' + charsArray.join('')  + '...';
isEllipsed = true;
} else {
break;
}
}


if (!isEllipsed) {
activePhrase = el.innerHTML;


let phraseArr = activePhrase.split('');
phraseArr = phraseArr.slice(0, phraseArr.length - 3)
el.innerHTML = phraseArr.join('') + '...';
}
}


let el = document.getElementById('ellipsed');


ellipsizeTextBox(el);

编辑: 遇到了 刮胡子,这是 JS 插件,多行文本截断基于给定的最大高度真的很好。它使用二进制搜索来寻找最佳断点。绝对值得调查。


原答案:

我不得不为这个问题想出一个普通的 JS 解决方案。在我所处理的案例中,我必须将一个长的产品名称放入有限的宽度中,放在两行以上; 如果需要,可以使用省略号截断。

我利用各种职位的回答来编造一些符合我需要的东西,策略如下:

  1. 计算所需字体大小的字体变体的平均字符宽度。
  2. 计算容器的宽度
  3. 计算容器中适合一行的字符数
  4. 根据一行上适合的字符数和文本应该换行的行数计算截断字符串的字符数。
  5. 根据前面的计算截断输入文本(考虑到省略号添加的额外字符) ,并在末尾附加“ ...”

代码示例:

/**
* Helper to get the average width of a character in px
* NOTE: Ensure this is used only AFTER font files are loaded (after page load)
* @param {DOM element} parentElement
* @param {string} fontSize
*/
function getAverageCharacterWidth(parentElement, fontSize) {
var textSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()";
parentElement = parentElement || document.body;
fontSize = fontSize || "1rem";
var div = document.createElement('div');
div.style.width = "auto";
div.style.height = "auto";
div.style.fontSize = fontSize;
div.style.whiteSpace = "nowrap";
div.style.position = "absolute";
div.innerHTML = textSample;
parentElement.appendChild(div);


var pixels = Math.ceil((div.clientWidth + 1) / textSample.length);
parentElement.removeChild(div);
return pixels;
}


/**
* Helper to truncate text to fit into a given width over a specified number of lines
* @param {string} text Text to truncate
* @param {string} oneChar Average width of one character in px
* @param {number} pxWidth Width of the container (adjusted for padding)
* @param {number} lineCount Number of lines to span over
* @param {number} pad Adjust this to ensure optimum fit in containers. Use a negative value to Increase length of truncation, positive values to decrease it.
*/
function truncateTextForDisplay(text, oneChar, pxWidth, lineCount, pad) {
var ellipsisPadding = isNaN(pad) ? 0 : pad;
var charsPerLine = Math.floor(pxWidth / oneChar);
var allowedCount = (charsPerLine * (lineCount)) - ellipsisPadding;
return text.substr(0, allowedCount) + "...";
}




//SAMPLE USAGE:
var rawContainer = document.getElementById("raw");
var clipContainer1 = document.getElementById("clip-container-1");
var clipContainer2 = document.getElementById("clip-container-2");


//Get the text to be truncated
var text=rawContainer.innerHTML;


//Find the average width of a character
//Note: Ideally, call getAverageCharacterWidth only once and reuse the value for the same font and font size as this is an expensive DOM operation
var oneChar = getAverageCharacterWidth();


//Get the container width
var pxWidth = clipContainer1.clientWidth;


//Number of lines to span over
var lineCount = 2;


//Truncate without padding
clipContainer1.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount);


//Truncate with negative padding value to adjust for particular font and font size
clipContainer2.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount,-10);
.container{
display: inline-block;
width: 200px;
overflow: hidden;
height: auto;
border: 1px dotted black;
padding: 10px;
}
<h4>Untruncated</h4>
<div id="raw" class="container">
This is super long text which needs to be clipped to the correct length with ellipsis spanning over two lines
</div>
<h4>Truncated</h4>
<div id="clip-container-1" class="container">
</div>
<h4>Truncated with Padding Tweak</h4>
<div id="clip-container-2" class="container">
</div>

附注:

  1. 如果截断只在一行上,那么使用 text-overflow: 省略号的纯 CSS 方法更为简洁
  2. 没有固定宽度的字体可能会导致截断发生得太早或太晚(因为不同的字符有不同的宽度)。在某些情况下,使用 pad 参数有助于缓解这种情况,但并非万无一失:)
  3. 将添加在链接和原来的职位后,我得到笔记本电脑回(需要历史)的参考

PPS: 刚刚意识到这与@DanMan 和@st 建议的方法非常相似。永远不会。有关实现示例,请检查代码段。

在我的场景中,我不能使用上面提到的任何函数,而且我还需要告诉函数不管字体大小或容器大小要显示多少行。

我的解决方案基于使用 画布,测量,文本方法(这是一个 HTML5特性) ,正如 多米解释的 给你,所以它不是完全跨浏览器的。

你可以在 这把小提琴上看到它是如何工作的。

这是密码:

var processTexts = function processTexts($dom) {
var canvas = processTexts .canvas || (processTexts .canvas = document.createElement("canvas"));


$dom.find('.block-with-ellipsis').each(function (idx, ctrl) {
var currentLineAdded = false;
var $this = $(ctrl);


var font = $this.css('font-family').split(",")[0]; //This worked for me so far, but it is not always so easy.
var fontWeight = $(this).css('font-weight');
var fontSize = $(this).css('font-size');
var fullFont = fontWeight + " " + fontSize + " " + font;
// re-use canvas object for better performance
var context = canvas.getContext("2d");
context.font = fullFont;


var widthOfContainer = $this.width();
var text = $.trim(ctrl.innerHTML);
var words = text.split(" ");
var lines = [];
//Number of lines to span over, this could be calculated/obtained some other way.
var lineCount = $this.data('line-count');


var currentLine = words[0];
var processing = "";


var isProcessing = true;
var metrics = context.measureText(text);
var processingWidth = metrics.width;
if (processingWidth > widthOfContainer) {
for (var i = 1; i < words.length && isProcessing; i++) {
currentLineAdded = false;
processing = currentLine + " " + words[i];
metrics = context.measureText(processing);
processingWidth = metrics.width;
if (processingWidth <= widthOfContainer) {
currentLine = processing;
} else {
if (lines.length < lineCount - 1) {
lines.push(currentLine);
currentLine = words[i];
currentLineAdded = true;
} else {
processing = currentLine + "...";
metrics = context.measureText(processing);
processingWidth = metrics.width;
if (processingWidth <= widthOfContainer) {
currentLine = processing;
} else {
currentLine = currentLine.slice(0, -3) + "...";
}
lines.push(currentLine);
isProcessing = false;
currentLineAdded = true;
}
}
}
if (!currentLineAdded)
lines.push(currentLine);
ctrl.innerHTML = lines.join(" ");
}
});
};


(function () {
$(document).ready(function () {
processTexts($(document));
});
})();

使用它的 HTML 应该是这样的:

<div class="block-with-ellipsis" data-line-count="2">
VERY LONG TEXT THAT I WANT TO BREAK IN LINES. VERY LONG TEXT THAT I WANT TO BREAK IN LINES.
</div>

获得 font-family 的代码相当简单,在我的例子中可以工作,但是对于更复杂的场景,您可能需要使用 这些线中的一些代码。

另外,在我的例子中,我告诉函数要使用多少行,但是您可以根据容器大小和字体计算要显示多少行。

这里我用更快的算法做了另一个库,请检查:

Https://github.com/i-ahmed-biz/fast-ellipsis

使用 Bower 安装:

bower install fast-ellipsis

使用 npm 安装:

npm install fast-ellipsis

希望你喜欢!

也许很晚了,但是使用 SCSS 你可以声明一个像这样的函数:

@mixin clamp-text($lines, $line-height) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lines;
line-height: $line-height;
max-height: unquote('#{$line-height*$lines}em');


@-moz-document url-prefix() {
position: relative;
height: unquote('#{$line-height*$lines}em');


&::after {
content: '';
text-align: right;
position: absolute;
bottom: 0;
right: 0;
width: 30%;
height: unquote('#{$line-height}em');
background: linear-gradient(
to right,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 1) 50%
);
}
}
}

像这样使用它:

.foo {
@include clamp-text(1, 1.4);
}

它将把文本截断为一行,并且知道它的行高是1.4

火狐

enter image description here

铬合金

enter image description here

非常简单的 javascript 解决方案:

.croppedTexts {
max-height: 32px;
overflow: hidden;
}

还有 JS:

var list = document.body.getElementsByClassName("croppedTexts");
for (var i = 0; i < list.length; i++) {
cropTextToFit(list[i]);
}


function cropTextToFit (o) {
var lastIndex;
var txt = o.innerHTML;
if (!o.title) o.title = txt;


while (o.scrollHeight > o.clientHeight) {
lastIndex = txt.lastIndexOf(" ");
if (lastIndex == -1) return;
txt = txt.substring(0, lastIndex);
o.innerHTML = txt + "…";
}
}

我已经做了一个版本,保持了 html 的完整性。 举个例子

JQuery

function shorten_text_to_parent_size(text_elem) {
textContainerHeight = text_elem.parent().height();




while (text_elem.outerHeight(true) > textContainerHeight) {
text_elem.html(function (index, text) {
return text.replace(/(?!(<[^>]*>))\W*\s(\S)*$/, '...');
});


}
}


$('.ellipsis_multiline').each(function () {
shorten_text_to_parent_size($(this))
});

CSS

.ellipsis_multiline_box {
position: relative;
overflow-y: hidden;
text-overflow: ellipsis;
}

举个例子

我写了一个角度分量来解决这个问题。它将给定的文本分割成 span 元素。呈现之后,它删除所有溢出的元素,并将省略号放在最后一个可见元素之后。

用法例子:

<app-text-overflow-ellipsis [text]="someText" style="max-height: 50px"></app-text-overflow-ellipsis>

Stackblitz 演示: https://stackblitz.com/edit/angular-wfdqtd

组成部分:

import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef, HostListener,
Input,
OnChanges,
ViewChild
} from '@angular/core';


@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-text-overflow-ellipsis',
template: `
<span *ngFor="let word of words; let i = index" [innerHTML]="word + (!endsWithHyphen(i) ? ' ' : '')"></span>
<span #ellipsis [hidden]="!showEllipsis && !initializing" [class.initializing]="initializing" [innerHTML]="'...' + (initializing ? '&nbsp;' : '')"></span>
`,
styles: [`
:host {
display: block;
position: relative;
}
.initializing {
opacity: 0;
}
`
]
})


export class TextOverflowEllipsisComponent implements OnChanges {
@Input()
text: string;


showEllipsis: boolean;
initializing: boolean;


words: string[];


@ViewChild('ellipsis')
ellipsisElement: ElementRef;


constructor(private element: ElementRef, private cdRef: ChangeDetectorRef) {}


ngOnChanges(){
this.init();
}


@HostListener('window:resize')
init(){
// add space after hyphens
let text = this.text.replace(/-/g, '- ') ;


this.words = text.split(' ');
this.initializing = true;
this.showEllipsis = false;
this.cdRef.detectChanges();


setTimeout(() => {
this.initializing = false;
let containerElement = this.element.nativeElement;
let containerWidth = containerElement.clientWidth;
let wordElements = (<HTMLElement[]>Array.from(containerElement.childNodes)).filter((element) =>
element.getBoundingClientRect && element !== this.ellipsisElement.nativeElement
);
let lines = this.getLines(wordElements, containerWidth);
let indexOfLastLine = lines.length - 1;
let lineHeight = this.deductLineHeight(lines);
if (!lineHeight) {
return;
}
let indexOfLastVisibleLine = Math.floor(containerElement.clientHeight / lineHeight) - 1;


if (indexOfLastVisibleLine < indexOfLastLine) {


// remove overflowing lines
for (let i = indexOfLastLine; i > indexOfLastVisibleLine; i--) {
for (let j = 0; j < lines[i].length; j++) {
this.words.splice(-1, 1);
}
}


// make ellipsis fit into last visible line
let lastVisibleLine = lines[indexOfLastVisibleLine];
let indexOfLastWord = lastVisibleLine.length - 1;
let lastVisibleLineWidth = lastVisibleLine.map(
(element) => element.getBoundingClientRect().width
).reduce(
(width, sum) => width + sum, 0
);
let ellipsisWidth = this.ellipsisElement.nativeElement.getBoundingClientRect().width;
for (let i = indexOfLastWord; lastVisibleLineWidth + ellipsisWidth >= containerWidth; i--) {
let wordWidth = lastVisibleLine[i].getBoundingClientRect().width;
lastVisibleLineWidth -= wordWidth;
this.words.splice(-1, 1);
}




this.showEllipsis = true;
}
this.cdRef.detectChanges();


// delay is to prevent from font loading issues
}, 1000);


}


deductLineHeight(lines: HTMLElement[][]): number {
try {
let rect0 = lines[0][0].getBoundingClientRect();
let y0 = rect0['y'] || rect0['top'] || 0;
let rect1 = lines[1][0].getBoundingClientRect();
let y1 = rect1['y'] || rect1['top'] || 0;
let lineHeight = y1 - y0;
if (lineHeight > 0){
return lineHeight;
}
} catch (e) {}


return null;
}


getLines(nodes: HTMLElement[], clientWidth: number): HTMLElement[][] {
let lines = [];
let currentLine = [];
let currentLineWidth = 0;


nodes.forEach((node) => {
if (!node.getBoundingClientRect){
return;
}


let nodeWidth = node.getBoundingClientRect().width;
if (currentLineWidth + nodeWidth > clientWidth){
lines.push(currentLine);
currentLine = [];
currentLineWidth = 0;
}
currentLine.push(node);
currentLineWidth += nodeWidth;
});
lines.push(currentLine);


return lines;
}


endsWithHyphen(index: number): boolean {
let length = this.words[index].length;
return this.words[index][length - 1] === '-' && this.words[index + 1] && this.words[index + 1][0];
}
}

可以对 div使用 -webkit-line-clamp属性。

-webkit-line-clamp: <integer>表示在截断内容之前设置最大行数,然后在最后一行的末尾显示一个省略号 (…)

div {
width: 205px;
height: 40px;
background-color: gainsboro;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
  

/* <integer> values */
-webkit-line-clamp: 2;
}
<div>This is a multi-lines text block, some lines inside the div, while some outside</div>

Adrien Be 的回答中找到了这个简短的 只有 CSS解决方案:

.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}

二零二零年三月开始,浏览器支持95.3% ,IE 和 Opera Mini 都不支持。适用于 Chrome,Safari,Firefox 和 Edge。

只需为您的 class 或 html 标记添加这些 CSS,

.YOUR_CLASS_NAME {
width: 300px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}

如果仍然不工作,请参考此文件: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp

CSS 线夹紧是解决多线截断问题最简单的方法。

所有的主流浏览器都支持线夹,但是在 Webkit 中稍微有些不支持。框的底部边缘应始终与最后一行文本的底部对齐。Webkit 在省略时,将框的底部与省略行的“文本底部”对齐,而不是与真正的底部对齐。对于较大的 line-height值,这种差异变得非常明显。

在 Safari 中运行以下代码片段查看问题。

div {
border: 10px solid black;
line-height: 2;


display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
<div>Vel mollis dignissim duis ligula nisl tincidunt eget aliquet eget efficitur quis justo integer pellentesque lacus. Viverra libero viverra finibus. Non id diam lorem ipsum dolor sit amet. Consectetur adipiscing elit sed ac eleifend lorem et placerat. Ante vestibulum. Congue augue vel turpis aliquet. Urna a varius urna convallis sed integer dapibus lobortis enim non venenatis orci varius ut fusce porttitor eros vel. Mollis dignissim duis ligula nisl tincidunt eget aliquet eget efficitur.</div>

Fortunately, the element's height can easily be corrected with a bit of JavaScript.

if (navigator.vendor === "Apple Computer, Inc.") {
const {lineHeight} = window.getComputedStyle(element);
if (lineHeight.endsWith("px")) {
const line_height_px = parseFloat(lineHeight);
element.style.height = line_height_px * Math.ceil(
element.clientHeight / line_height_px
) + "px";
}
}