Я сделал образ для этого вопроса, чтобы его было легче понять.
Можно ли создать многоточие <div>
с фиксированной шириной и несколькими строками?
Я попробовал несколько плагинов jQuery, но не могу найти тот, который ищу. Любая рекомендация? Идеи?
Я сделал образ для этого вопроса, чтобы его было легче понять.
Можно ли создать многоточие <div>
с фиксированной шириной и несколькими строками?
Я попробовал несколько плагинов jQuery, но не могу найти тот, который ищу. Любая рекомендация? Идеи?
Ответы:
Просто быстрая основная идея.
Я тестировал со следующей разметкой:
<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 результат остается «визуально правильным» (конечно, без «...»).
Если вы объедините это с разумным усечением на стороне сервера (которое оставляет только небольшие накладные расходы), то оно будет работать быстрее :).
Опять же, это не полное решение, просто идея.
UPDATE: Добавлен jsFiddle Demo .
li
и внутри каждого из них есть .block
и, .block h2
и мне нужно применить это к h2
внутренней части, .block
но я не мог заставить его работать. Есть ли разница, если их больше одного .block h2
?
height*3
на несколько пикселей. Легко исправить, просто добавив несколько пикселей кdivh
while
строки:if(!$p.text().match(/\W*\s(\S)*$/)) break;
while ((upper-lower)>1) {let middle=((lower+upper)/2)|0 /*|0 is quick floor*/; if (test(words.slice(0,middle)+'...')) {lower=middle;} else {upper=middle;}}
. Как обнаружил @KrisWebDev, вы также захотите проверить одно гигантское слово.
Попробуйте плагин jQuery.dotdotdot .
$(".ellipsis").dotdotdot();
Обратите внимание, что «зажим линии» также обозначается как «Многоточие на блоке из нескольких линий» или «Вертикальный многоточие».
github.com/BeSite/jQuery.dotdotdot
github.com/josephschmitt/Clamp.js
Вот еще несколько, которые я еще не исследовал:
Есть несколько CSS-решений, но простейшее использование, -webkit-line-clamp
которое плохо поддерживает браузер . Смотрите живую демонстрацию на jsfiddle.net/AdrienBe/jthu55v7/
Многие люди приложили огромные усилия, чтобы сделать это, используя только CSS. Смотрите статьи и вопросы об этом:
Будь проще. Если у вас нет достаточно времени, чтобы посвятить себя этой функции, воспользуйтесь самым простым и проверенным решением: простым CSS или хорошо протестированной библиотекой javascript.
Пойдите для чего-то необычного / сложного / высоко настроенного, и вы заплатите цену за это в будущем.
Хорошим решением может стать исчезновение, как у Airbnb. Это, вероятно, базовый CSS в сочетании с базовым jQuery. На самом деле, это выглядит очень похоже на это решение на CSSTricks
Да, и если вы ищете дизайн вдохновения:
В HTML нет такой возможности, и это очень расстраивает.
Я разработал библиотеку, чтобы справиться с этим.
Проверьте мой сайт для скриншота, учебника и ссылки для скачивания.
Чистое решение JS, основанное на решении bažmegakapa, и некоторая очистка для учета людей, которые пытаются дать высоту / максимальную высоту, которая меньше, чем у элемента lineHeight:
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 «fade» и выглядит «блеклым». Если текст короткий, исчезновение не видно, поэтому проблем нет. Два контейнера можно настроить таким образом, чтобы одна или несколько строк отображались путем установки высоты контейнера, кратной высоте текстовой строки. Div "Fade" может быть расположен только для последней строки.
Вот простой способ CSS сделать это: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/
Вот резюме:
<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>
Вы можете использовать -webkit-line-clamp
собственность с div
.
-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>
Вот ванильное решение 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) + '…';
++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>
Вы можете поиграть с ним в коде ниже. Попробуйте изменить размер шрифта на панели CSS и внесите незначительные изменения в панель HTML (например, добавить дополнительное место где-нибудь), чтобы обновить результаты. Независимо от размера шрифта средний абзац всегда должен быть обрезан до количества строк во втором параметре, переданном в limitLines ().
Codepen: http://codepen.io/thdoan/pen/BoXbEK
РЕДАКТИРОВАТЬ: наткнулся на Shave, который является плагином JS, который действительно хорошо выполняет многострочное усечение текста на основе заданной максимальной высоты. Он использует бинарный поиск, чтобы найти оптимальную точку останова. Определенно стоит исследовать.
ОРИГИНАЛЬНЫЙ ОТВЕТ:
Мне пришлось придумать ванильное решение JS для этой проблемы. В случае, над которым я работал, мне приходилось помещать длинное название продукта в ограниченную ширину и в две строки; усеченный многоточием, если необходимо.
Я использовал ответы из разных постов SO, чтобы приготовить то, что соответствует моим потребностям. Стратегия заключается в следующем:
Пример кода:
/**
* 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>
PS:
PPS: Просто понял, что это очень похоже на подход, предложенный @DanMan и @ st.never. Извлеките фрагменты кода для примера реализации.
Очень простое решение для JavaScript. Divs должен быть стилизован, например:
.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 + "…";
}
}
Не точный ответ на вопрос, но я наткнулся на эту страницу, когда пытался сделать очень похожее, но хотел добавить ссылку «больше», а не просто многоточие. Это функция jQuery, которая добавляет ссылку «еще» к тексту, который переполняет контейнер. Лично я использую это с Bootstrap, но, конечно, он будет работать без.
Чтобы использовать, поместите свой текст в контейнер следующим образом:
<div class="more-less">
<div class="more-block">
<p>The long text goes in here</p>
</div>
</div>
Когда добавлена следующая функция jQuery, любой из div, который больше значения регулировки высоты, будет усечен и будет добавлена ссылка «Дополнительно».
$(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
Упомянутый плагин dotdotdot jQuery хорошо работает с angular:
(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>
Когда я искал решение проблемы многострочного эллипса, я удивился, что без jQuery нет ничего хорошего. Также есть несколько решений, основанных на цикле while, но я думаю, что они не эффективны и опасны из-за возможности попасть в бесконечный цикл. Итак, я написал этот код:
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);
Может быть, довольно поздно, но используя 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 его высота строки. Ожидаемый результат: Chrome для рендеринга ...
в конце и FF с небольшим затуханием в конце
Fire Fox
Хром
Нашел это короткое CSS- решение в ответе Adrien Be :
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
По состоянию на март 2020 года поддержка браузеров составляет 95,3% , не поддерживается в IE и Opera Mini. Работает на Chrome, Safari, Firefox и Edge.
Вы вероятно не можете сделать это (в настоящее время?) Без шрифта фиксированной ширины, как Courier. При использовании шрифта фиксированной ширины каждая буква занимает одно и то же горизонтальное пространство, так что вы, вероятно, можете посчитать буквы и умножить результат на текущий размер шрифта в ems или exs. Тогда вам просто нужно проверить, сколько букв помещается на одной строке, а затем разбить их.
В качестве альтернативы, для шрифтов с нефиксированным шрифтом вы можете создать отображение для всех возможных символов (например, i = 2px, m = 5px) и затем выполнить математику. Хотя много ужасной работы.
Чтобы расширить решение @ DanMan: в случае, когда используются шрифты переменной ширины, вы можете использовать среднюю ширину шрифта. Это имеет две проблемы: 1) текст со слишком большим количеством символов W будет переполнен и 2) текст со слишком большим количеством символов I будет обрезан раньше.
Или вы могли бы выбрать подход в худшем случае и использовать ширину буквы «W» (которая, я считаю, самая широкая). Это устраняет проблему 1 выше, но усиливает проблему 2.
Другой подход может быть следующим: оставить overflow: clip
в div и добавить раздел с многоточием (может быть, другой div или изображение) с помощью float: right; position: relative; bottom: 0px;
(не проверено). Хитрость заключается в том, чтобы изображение появилось над концом текста.
Вы также можете отображать изображение только тогда, когда знаете, что оно будет переполнено (скажем, после примерно 100 символов)
overflow: clip
? И что вы ожидаете от CSS float
?
С этим кодом нет необходимости в дополнительном элементе-обёртке, если высота элемента ограничена стилем max-height.
// 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;
}
В моем сценарии я не мог заставить работать ни одну из функций, упомянутых выше, и мне также нужно было указать функции, сколько строк показать, независимо от размера шрифта или размера контейнера.
Я основано мое решение по использованию Canvas.measureText метода (whic является 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>
Код , чтобы получить семейство шрифтов довольно просто, и в моем случае работает, но для более сложных сценариев , возможно , потребуется использовать что - то вдоль этих линий .
Кроме того, в моем случае я сообщаю функции, сколько строк использовать, но вы можете рассчитать, сколько строк показать в соответствии с размером контейнера и шрифтом.
Я сделал версию, которая оставляет HTML нетронутым. пример jsfiddle
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 ? ' ' : '')"></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];
}
}
Здесь я сделал еще одну библиотеку с более быстрым алгоритмом. Пожалуйста, проверьте:
https://github.com/i-ahmed-biz/fast-ellipsis
Чтобы установить с помощью Bower:
bower install fast-ellipsis
Чтобы установить с помощью npm:
npm install fast-ellipsis
Надеемся, вам понравится!
не уверен, что это то, что вы ищете, он использует минимальную высоту вместо высоты.
<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>
Очень простой функционал подойдет.
Директива:
$scope.truncateAlbumName = function (name) {
if (name.length > 36) {
return name.slice(0, 34) + "..";
} else {
return name;
}
};
Посмотреть:
<#p>{{truncateAlbumName(album.name)}}<#/p>
iiiiiiiiii
Против MMMMMMMMMM
(хотя текущий шрифт не так видим, хотя: D).