Багаторядковий текстовий переповнювач із перехресним браузером із еліпсисом, доданим у межах фіксованої ширини та висоти


178

Я зробив образ для цього питання, щоб полегшити його розуміння.

Чи можливо створити еліпсис на а <div>з фіксованою шириною та кількома лініями?

переповнення тексту

Я пробував тут і там кілька плагінів jQuery, але не можу знайти того, кого шукаю. Будь-яка рекомендація? Ідеї?



1
and stackoverflow.com/questions/3922739/… для рішення лише для css
Євген

пов'язана стаття css-tricks.com/line-clampin
Adrien Be

2
Для тих, хто шукає цього в середині 2016 року, коротка відповідь: ні, це не можливо в елегантному, крос-браузері, тільки в CSS. Рішення, яке часто дають якнайближче до завершення ( codepen.io/romanrudenko/pen/ymHFh ), настільки Goldbergian, що робить все ваше тіло боляче, і все ще досить потворно.
конрад

Відповіді:


91

Просто швидка основна ідея.

Я тестував із такою розміткою:

<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 результат залишається "візуально правильним" (без "..." звичайно).

Якщо ви поєднаєте це з розумним укороченням на стороні сервера (що залишає лише невеликі накладні витрати), воно запуститься швидше :).

Знову ж таки, це не повне рішення, а лише ідея.

ОНОВЛЕННЯ: Додано демонстрацію jsFiddle .


1
чудове рішення @bazmegakapa ... але у мене є проблеми, які намагаються адаптувати його до моєї справи. У мене різні liі всередині кожного з них є .blockі є, .block h2і мені потрібно застосувати це h2всередині, .blockале я не міг змусити його працювати. Чи є якісь різні, якщо їх більше .block h2?
Олексій

1
У моєму випадку це було лише 2 рядки тексту, коли їх повинно було бути 3. Очевидно, мій контейнер був меншим за рядок height*3на кілька пікселів. Просте виправлення - просто додати кілька пікселів доdivh
Lukas LT

3
У мене був поганий досвід нескінченного циклу з цим сценарієм, тому що текст містив лише одне дуже довге слово, тому замінюючий регулярний параметр ніколи не відповідав. Щоб уникнути цього, додайте цей код відразу після whileрядка:if(!$p.text().match(/\W*\s(\S)*$/)) break;
KrisWebDev

1
Незважаючи на те, що в цьому випадку не буде проблеми, оновлення DOM та повторна перевірка макета є поганою ідеєю, оскільки це може спричинити уповільнення. Щоб пом'якшити це, ви могли б зробити щось подібне до двійкового пошуку: перевірити, чи відповідає текстовий блок, інакше розділіть текст на слова чи символи та визначте межі (нижня = 1 слово / символи, верхня = всі слова / символи) , 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, вам також захочеться перевірити наявність одного гігантського слова.
Чіното Вокро

1
Це рішення чудове. У моєму випадку мені потрібно відслідковувати оригінальний текст, щоб я міг урізати повне значення тексту. Отже, коли сторінка завантажується, я зберігаю оригінальний текст у змінній, і перш ніж запустити цю логіку, я переконуюсь, що "оновіть" елемент початковим значенням тексту. Додайте дебютацію, і це чудово працює.
besseddrest

68

Спробуйте плагін jQuery.dotdotdot .

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

11
Як би ти це вимовив? dot-dot-dot-dot?
JackAce

58
Дійсно гарно використовувати> 600 рядків js для вирішення проблеми, яку має вирішити css
Jethro Larson

Я спробував це, і він працює чудово. Має бути прийнята відповідь
АбдельХаді

1
Працює, але обов'язково використовуйте window.loaded події, а не $ (документ) .ready (), оскільки шрифти та інші зовнішні ресурси можуть впливати на макет вашого HTML. Якщо dotdotdot виконує перед завантаженням ресурсів тез, текст не буде врізаний у правильному положенні.
sboisse

10
Це комерційний інструмент, він коштує 5 доларів для одного сайту і 35 доларів для декількох сайтів. Ліцензування було б болем. Я подумав, що це безкоштовно і негайно інтегрується, не так!
ген b.

29

Бібліотеки Javascript для "затискання рядків"

Зауважте, що "затискання ліній" також називається "Еліпсис на блоці багаторядкових" або "вертикальний еліпсис".


github.com/BeSite/jQuery.dotdotdot


github.com/josephschmitt/Clamp.js


Ось ще кілька, яких я ще не досліджував:


CSS-рішення для затискання ліній

Є кілька CSS-рішень, але найпростіші варіанти використання -webkit-line-clampмають погану підтримку браузера . Дивіться демонстрацію в прямому ефірі на jsfiddle.net/AdrienBe/jthu55v7/

Багато людей доклали великих зусиль для того, щоб це сталося лише за допомогою CSS. Дивіться статті та запитання про нього:


Що я б рекомендував

Не ускладнювати. Якщо у вас є багато часу, щоб присвятити цій функції, перейдіть до найпростішого і перевіреного рішення: простого CSS або добре перевіреної бібліотеки javascript.

Займіться чимось вигадливим / складним / дуже налаштованим і ви заплатите ціну за це вниз.


Що роблять інші

Зникнення, як Airbnb, може бути хорошим рішенням. Ймовірно, це базовий CSS у поєднанні з основним jQuery. Насправді це схоже на це рішення на CSSTricks

AirBnb "читати більше" рішення

О, і якщо ви шукаєте дизайнерські натхнення:

  • smashingmagazine.com/2009/07/designing-read-more-and-continue-reading-links/ , з 2009 року, хоча ...
  • Dribbble, напевно, має цікаві конструкції ... Я не зміг знайти спосіб зібрати їх, хоча (через пошук або теги), не соромтеся поділитися відповідним посиланням

6

У HTML такої функції немає, і це дуже засмучує.

Я розробив бібліотеку для вирішення цього питання.

  • Багаторядковий переповнення тексту: еліпсис
  • Багаторядковий текст із технологіями, які його не підтримують: SVG, Canvas, наприклад
  • У тексті SVG, у візуалізації HTML та у експорті у PDF, наприклад, мають однакові розриви рядків

Ознайомтесь із моїм сайтом щодо скріншоту, підручника та посилання для завантаження.


"помилка встановлення db-зв’язку" ... ви можете зробити так, як усі, і розмістити ваш проект на Github, можливо, це буде приємніше для вас і для спільноти :)
Adrien Be

@AdrienBe це на Github: github.com/rossille/jstext , і ти маєш рацію, github стабільніше, ніж мій веб-сайт, я встановив сторінку github як основну посилання
Самуель Россіль

@SamuelRossille чудова новина, дякую за швидке оновлення!
Адріан Бе

4

Чисте рішення JS, засноване на рішенні bažmegakapa, і деяке очищення для обліку людей, які намагаються надати висоту / максимальну висоту, меншу за лінію елемента Висота:

  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);

Це дуже неефективний код. До речі, використання "while" look - це потенційна помилка з нескінченним циклом.
WebBrother

4

У мене є рішення, яке працює добре, але замість еліпсису він використовує градієнт. Переваги полягають у тому, що вам не потрібно робити жодних обчислень JavaScript, і він працює для контейнерів змінної ширини, включаючи комірки таблиці. Він використовує пару зайвих дівок, але це дуже просто здійснити.

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

Редагувати: Вибачте, я не знав, що посилання недостатньо. Рішення полягає в тому, щоб розмістити div навколо тексту та надати стиль div для управління переповненням. Всередині div помістіть ще один div з градієнтом "згасання", який можна зробити за допомогою CSS або зображення (для старого IE). Градієнт переходить від прозорого до фонового кольору комірки таблиці і трохи ширший за еліпсис. Якщо текст довгий і переповнюється, він переходить під діву «зів’ялий» і виглядає «зів’ялою». Якщо текст короткий, зникнення невидиме, тому проблем немає. Два контейнери можуть бути відрегульовані таким чином, щоб відображалася одна або кілька ліній, встановлюючи висоту контейнера як кратну висоті рядка тексту. Див "fade" може бути розміщений лише для останнього рядка.


Будь ласка, поділіться важливими частинами вашого рішення, оскільки відповіді, що стосуються лише посилань, заборонені.
капа

блискучим аспектом цього є те, що сам текст не врізаний, тому, якщо користувач копіює та вставляє таблицю, з’являється весь вміст.
прототип

Дуже приємна концепція. Про це також згадується у цій статті (спосіб «згасає»). Я вважаю, що css-tricks.com/line-clampin
Adrien Be

4

Ось чистий 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>

4

Ви можете використовувати -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>


2
Не впевнений, чому хтось спростував цю відповідь. Підтримка браузера станом на березень 2020 року є досить пристойною - 95% caniuse.com/#search=line-clamp
Юліан

2

Ось ванільне рішення 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>

Ви можете пограти з ним у кодексі нижче. Спробуйте змінити розмір шрифту на панелі CSS та внесіть незначні зміни на панелі HTML (наприклад, додайте десь додаткове місце), щоб оновити результати. Незалежно від розміру шрифту, середній абзац завжди має бути обрізаний кількістю рядків у другому параметрі, переданих до limitLines ().

Codepen: http://codepen.io/thdoan/pen/BoXbEK


2

EDIT: Натрапив на Shave, який є плагіном 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>

PS:

  1. Якщо обрізання має бути лише в одному рядку, чистий CSS метод використання переповнення тексту: еліпсис акуратніший
  2. Шрифти, які не мають фіксованої ширини, можуть спричинити усікання занадто рано чи занадто пізно (оскільки різні символи мають різну ширину). Використання параметра pad допомагає пом'якшити це в деяких випадках, але не буде дурним доказом :)
  3. Додаватимуть посилання та посилання на оригінальні повідомлення після повернення ноутбука (потрібна історія)

PPS: Щойно зрозумів, що це дуже схоже на підхід, запропонований @DanMan та @ st.never. Ознайомтесь із фрагментами коду для прикладу реалізації.


2

Дуже просте рішення Javascript. Divs повинні бути стилізовані fe:

.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 + "…";
  }
}

1

Не точна відповідь на питання, але я натрапив на цю сторінку, коли намагався зробити дуже подібне, але бажаючи додати посилання на "перегляд більше", а не просто напростий еліпсис. Це функція jQuery, яка додасть посилання "більше" до тексту, який переповнює контейнер. Особисто я використовую це з Bootstrap, але, звичайно, це буде працювати без цього.

Приклад більше скріншот

Для використання покладіть текст у контейнер таким чином:

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

Коли буде додана наступна функція jQuery, будь-який з дівок, які перевищують значення коригувальної висоти, буде усічений і матиме посилання "Більше".

$(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


<sigh> Я думав, що хтось може спростувати це, мабуть, тому, що це не точна відповідь на питання. Тим не менш, я сподіваюся, що хтось стане корисною, оскільки я не міг отримати цю інформацію ніде, і саме тут я закінчився після пошуку.
Енді Беверлі

1

Згадана dotdotdot 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>

1

Чиста демонстрація JS (без циклу jQuery та "while")

Коли я шукав рішення багатолінійної проблеми еліпсису, я здивувався, що жодного доброго немає без 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);

1

Можливо, досить пізно, але за допомогою 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 його висота рядка. Очікуваний вихід буде хромований ...на кінці та FF з прохолодним вицвітанням в кінці

Firefox

введіть тут опис зображення

Хром

введіть тут опис зображення



0

Напевно, ви не можете зробити це (наразі?) Без шрифту фіксованої ширини, як Courier. З шрифтом фіксованої ширини кожна літера займає однаковий горизонтальний простір, тому ви, ймовірно, могли порахувати літери та помножити результат на поточний розмір шрифту в ems чи exs. Тоді вам слід було б просто перевірити, скільки букв вміщується на одному рядку, а потім розбити його.

Крім того, для нефіксованих шрифтів ви зможете створити відображення для всіх можливих символів (наприклад, i = 2px, m = 5px), а потім виконати математику. Хоча багато негарної роботи.


0

Для розширення рішення @ DanMan: у випадку, коли використовуються шрифти змінної ширини, ви можете використовувати середню ширину шрифту. У цього є дві проблеми: 1) текст із занадто великою кількістю W буде переповнений і 2) текст із занадто великою кількістю я був би скорочений раніше.

Або ви можете скористатися найгіршим підходом і використовувати ширину літери "W" (яка, на мою думку, є найширшою). Це усуває проблему 1 вище, але посилює проблему 2.

Інший підхід може бути: залиште overflow: clipв діві і додайте розділ еліпсису (можливо, ще один div або зображення) з float: right; position: relative; bottom: 0px;(неперевірений). Хитрість полягає в тому, щоб зображення з’явилося над кінцем тексту.

Ви також можете відображати зображення лише тоді, коли знаєте, що воно переповнюється (скажімо, приблизно через 100 символів)


Що таке overflow: clip? І що б ви очікували від CSS float?
капа

0

З цим кодом немає необхідності в додатковому розгортанні, якщо елемент має його висоту, обмежену стилем макс-висоти.

// 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;
}

1
Це часто нескінченна петля.
Атадж

0

У моєму сценарії я не міг приступити до роботи жодної з функцій, згаданих вище, і мені також потрібно було вказати функції, скільки рядків відображати незалежно від розміру шрифту або розміру контейнера.

Я засновано моє рішення по використанню 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>

Код, щоб отримати сімейство шрифтів, досить простий, і в моєму випадку працює, але для складніших сценаріїв вам може знадобитися щось використовувати в цих напрямках .

Крім того, у моєму випадку я кажу функції, скільки рядків використовувати, але ви можете обчислити, скільки рядків відображати відповідно до розміру контейнера та шрифту.


0

Я зробив версію, яка залишає 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;
}

Приклад jsfiddle


0

Я написав кутовий компонент, який вирішує проблему. Він розбиває заданий текст на прольотні елементи. Після візуалізації він видаляє всі перелиті елементи та розміщує еліпсис відразу після останнього видимого елемента.

Приклад використання:

<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];
  }
}

0

Тут я зробив ще одну бібліотеку з більш швидким алгоритмом. Будь ласка, перевірте:

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

Щоб встановити за допомогою bower:

bower install fast-ellipsis

Щоб встановити за допомогою npm:

npm install fast-ellipsis 

Сподіваюся, вам сподобається!


-2

не впевнений, що це те, що ви шукаєте, він використовує мінімальну висоту замість висоти.

    <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>

4
Проблема полягає в тому, що у ваших кодах є номер 63, якщо число відомо, то все стає просто, просто функція усічення виконує цю роботу, як ваші коди. Однак як знати число? Іншими словами, як знати, де текст буде розбитий рядком? Якщо на це питання можна відповісти, то проблему можна просто вирішити в логіці "1, обчислити число; 2, усікати"
Едвард

-3

Дуже простий функція зробить.

Директива:

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

Вид:

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

3
Як ми вже обговорювали в інших відповідях, проблема у вашому коді - це число 36. Окрім того, що ваш код визначається певною шириною контейнера, він також не є точним: якщо шрифти не фіксованої ширини можуть бути великими різницями між літерами . Дивіться iiiiiiiiiivs MMMMMMMMMM(з поточним шрифтом не видно, хоча: D).
капа
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.