Overflow di testo su più righe tra browser con puntini di sospensione inseriti in una larghezza e altezza fisse


178

Ho creato un'immagine per questa domanda per facilitarne la comprensione.

È possibile creare un'ellissi su una <div>larghezza fissa e più linee?

text-trabocco

Ho provato alcuni plugin jQuery qua e là, ma non riesco a trovare quello che sto cercando. Qualche raccomandazione? Idee?



1
e stackoverflow.com/questions/3922739/… per soluzione solo per css
Evgeny


2
Per chiunque lo cerchi a metà 2016, la risposta breve è: NO, non è possibile in modo elegante, cross-browser, solo CSS. La soluzione spesso indicata come la più completa da completare ( codepen.io/romanrudenko/pen/ymHFh ) è talmente goldbergiana da far soffrire tutto il tuo corpo, e comunque piuttosto brutta.
Konrad,

Risposte:


91

Solo una rapida idea di base.

Stavo testando con il seguente markup:

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

E CSS:

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

Applicando questo jQuery otterrai il risultato desiderato:

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

Tenta ripetutamente di rimuovere l'ultima parola del testo fino a raggiungere la dimensione desiderata. A causa dell'overflow: nascosto; il processo rimane invisibile e anche con JS disattivato il risultato rimane 'visivamente corretto' (senza il "..." ovviamente).

Se lo combini con un sensibile troncamento sul lato server (che lascia solo un piccolo sovraccarico), funzionerà più velocemente :).

Ancora una volta, questa non è una soluzione completa, solo un'idea.

AGGIORNAMENTO: aggiunta una demo jsFiddle .


1
ottima soluzione @bazmegakapa ... ma ho dei problemi nel tentativo di adattarlo al mio caso. Ne ho diversi lie dentro ognuno c'è un .blocke un .block h2e devo applicarlo h2all'interno .blockma non riesco a farlo funzionare. È diverso se ce ne sono più di uno .block h2?
Alex

1
Nel mio caso stava lasciando solo 2 righe di testo quando avrebbero dovuto esserci 3. Apparentemente il mio contenitore era più piccolo della riga height*3di alcuni pixel. La soluzione semplice è semplicemente aggiungere alcuni pixel adivh
Lukas LT

3
Ho avuto una brutta esperienza di loop infinito con questo script perché il testo conteneva solo una parola molto lunga, quindi il regexp di sostituzione non ha mai eguagliato. Per evitare ciò, aggiungi questo codice subito dopo la whileriga:if(!$p.text().match(/\W*\s(\S)*$/)) break;
KrisWebDev

1
Sebbene in questo caso non sia un problema, aggiornare il DOM e controllare ripetutamente il layout è una cattiva idea perché potrebbe causare un rallentamento. Per mitigare questo, potresti fare qualcosa di simile a una ricerca binaria: prova per vedere se il blocco di testo si adatta già, altrimenti dividi il testo in parole o caratteri e definisci i tuoi limiti (inferiore = 1 parola / caratteri, superiore = tutte le parole / caratteri) , 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;}}. Come ha scoperto @KrisWebDev, vorrai anche cercare una parola gigante.
Chinoto Vokro,

1
Questa soluzione è fantastica. Nel mio caso, devo tenere traccia del testo originale in modo da poter troncare il valore del testo completo in modo reattivo. Pertanto, quando la pagina viene caricata, memorizzo il testo originale in una variabile e prima di eseguire questa logica mi assicuro di "aggiornare" l'elemento con il valore di testo originale. Aggiungi debounce e funziona meravigliosamente.
besseddrest,

68

Prova il plug-in jQuery.dotdotdot .

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

11
Come lo pronunceresti? dot-dot-dot-dot?
JackAce,

58
Fa davvero schifo usare> 600 righe di js per risolvere un problema che dovrebbe essere risolto da css
Jethro Larson

L'ho provato e funziona benissimo. Dovrebbe essere la risposta accettata
AbdelHady,

1
Funziona ma assicurati di utilizzare l'evento window.loaded e non $ (document) .ready (), poiché i font e altre risorse esterne potrebbero influire sul layout del tuo HTML. Se dotdotdot viene eseguito prima che queste risorse vengano caricate, il testo non verrà troncato nella posizione corretta.
sboisse,

10
Questo è uno strumento commerciale, costa $ 5 per un singolo sito e $ 35 per più siti. La concessione di licenze sarebbe una seccatura. Ho pensato che fosse gratuito e immediatamente integrabile, non così!
gene b.

29

Librerie Javascript per "line clamping"

Si noti che il "bloccaggio della linea" viene anche definito "Ellisse sul blocco di linee multiple" o "ellissi verticali".


github.com/BeSite/jQuery.dotdotdot


github.com/josephschmitt/Clamp.js

  • Contro: code repo a malapena attivo
  • reusablebits informativa.com/post/2980974411/clamp-js-v0-2-explanations-and-performance

Eccone alcuni che non ho ancora indagato:


Soluzioni CSS per il bloccaggio della linea

Esistono alcune soluzioni CSS, ma gli usi più semplici -webkit-line-clampche hanno uno scarso supporto del browser . Guarda la demo live su jsfiddle.net/AdrienBe/jthu55v7/

Molte persone hanno fatto grandi sforzi per far sì che ciò accadesse usando solo CSS. Vedi articoli e domande a riguardo:


Quello che consiglierei

Mantienilo semplice. A meno che tu non abbia molto tempo da dedicare a questa funzione, scegli la soluzione più semplice e testata: CSS semplice o una libreria javascript ben collaudata.

Scegli qualcosa di elegante / complesso / altamente personalizzato e pagherai il prezzo per questo lungo la strada.


Quello che fanno gli altri

Svanire come fa Airbnb potrebbe essere una buona soluzione. Probabilmente è CSS di base accoppiato con jQuery di base. In realtà, sembra molto simile a questa soluzione su CSSTricks

Soluzione AirBnb "leggi di più"

Oh, e se cerchi ispirazioni di design:


6

Non esiste una tale funzionalità in HTML, e questo è molto frustrante.

Ho sviluppato una biblioteca per affrontare questo.

  • Overflow di testo multilinea: puntini di sospensione
  • Testo multilinea con tecnologie che non lo supportano: SVG, Canvas ad esempio
  • Esistono esattamente le stesse interruzioni di riga nel testo SVG, nel rendering HTML e nell'esportazione in PDF, ad esempio

Controlla il mio sito per screenshot, tutorial e link per il download.


"errore nello stabilire una connessione db" ... potresti voler fare come tutti gli altri e ospitare il tuo progetto su Github, probabilmente sarebbe più bello per te e per la comunità :)
Adrien Be

@AdrienBe è su Github: github.com/rossille/jstext , e hai ragione, essendo github più stabile del mio sito web, ho impostato la pagina github come link principale
Samuel Rossille,

@SamuelRossille ottime notizie, grazie per l'aggiornamento rapido!
Adrien, l'

4

Pura soluzione JS basata sulla soluzione di bažmegakapa e un po 'di pulizia per tenere conto delle persone che cercano di dare un'altezza / altezza massima inferiore alla linea dell'elemento Altezza:

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

Questo è un codice molto inefficace. A proposito l'uso di "while" look è un potenziale bug con loop infinito.
WebBrother,

4

Ho una soluzione che funziona bene ma invece un'ellissi utilizza un gradiente. I vantaggi sono che non è necessario eseguire calcoli JavaScript e funziona con contenitori a larghezza variabile, comprese le celle di tabella. Utilizza un paio di div extra, ma è molto facile da implementare.

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

Modifica: scusa, non sapevo che il link non fosse abbastanza. La soluzione è mettere un div attorno al testo e modellare il div per controllare l'overflow. All'interno del div inserisci un altro div con un gradiente "dissolvenza" che può essere creato utilizzando CSS o un'immagine (per IE precedente). Il gradiente va dal colore trasparente al colore di sfondo della cella della tabella ed è un po 'più largo di un puntino di sospensione. Se il testo è lungo e trabocca, passa sotto il div "dissolvenza" e appare "sfumato". Se il testo è breve, la dissolvenza è invisibile, quindi non ci sono problemi. I due contenitori possono essere regolati per mostrare una o più righe impostando l'altezza del contenitore come multiplo dell'altezza della riga di testo. Il div "dissolvenza" può essere posizionato in modo da coprire solo l'ultima riga.


Si prega di condividere le parti importanti della soluzione, su SO solo le risposte al link non sono consentite.
Kapa,

l'aspetto brillante di questo è che il testo stesso non viene troncato, quindi se l'utente copia e incolla la tabella l'intero contenuto appare.
prototipo

Un concetto molto carino. È anche menzionato in questo articolo (modo "dissolvenza") Credo che css-tricks.com/line-clampin
Adrien Be

4

Ecco un puro modo CSS per ottenere questo risultato: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/

Ecco un riassunto:

inserisci qui la descrizione dell'immagine

<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

Puoi usare la -webkit-line-clampproprietà con div.

-webkit-line-clamp: <integer>il che significa impostare il numero massimo di righe prima di troncare il contenuto e quindi visualizzare i puntini (…)di sospensione alla fine dell'ultima riga.

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
Non sono sicuro del motivo per cui qualcuno ha votato in negativo questa risposta. Il supporto del browser a partire da marzo 2020 è abbastanza decente - 95% caniuse.com/#search=line-clamp
Yulian

2

Ecco una soluzione JavaScript vaniglia che puoi usare in un pizzico:

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

Puoi giocarci intorno nel codice qui sotto. Prova a cambiare la dimensione del carattere nel pannello CSS ed esegui una modifica minore nel pannello HTML (ad esempio, aggiungi uno spazio aggiuntivo da qualche parte) per aggiornare i risultati. Indipendentemente dalla dimensione del carattere, il paragrafo centrale deve sempre essere troncato al numero di righe nel secondo parametro passato a limitLines ().

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


2

EDIT: Came across Shave, che è un plug-in JS che esegue molto bene il troncamento del testo su più righe basato su una determinata altezza massima. Utilizza la ricerca binaria per trovare il punto di interruzione ottimale. Sicuramente vale la pena indagare.


RISPOSTA ORIGINALE:

Ho dovuto trovare una soluzione JS alla vaniglia per questo problema. Nel caso in cui avessi lavorato, dovevo inserire un nome di prodotto lungo in larghezza limitata e su due righe; troncato da ellissi se necessario.

Ho usato le risposte di vari post SO per preparare qualcosa che si adattasse alle mie esigenze. La strategia è la seguente:

  1. Calcola la larghezza media dei caratteri della variante di carattere per la dimensione del carattere desiderata.
  2. Calcola la larghezza del contenitore
  3. Calcola il numero di caratteri che si adattano su una riga nel contenitore
  4. Calcola il numero di caratteri a cui troncare la stringa in base al numero di caratteri che si adattano a una riga e al numero di righe che il testo dovrebbe avvolgere.
  5. Tronca il testo di input in base al calcolo precedente (tenendo conto dei caratteri aggiuntivi aggiunti con i puntini di sospensione) e aggiungi "..." alla fine

Esempio di codice:

/**
 * 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. Se il troncamento deve essere su una sola riga, il puro metodo CSS di usare il testo overflow: ellissi è più ordinato
  2. I caratteri che non hanno una larghezza fissa possono causare il troncamento troppo presto o troppo tardi (poiché caratteri diversi hanno larghezze diverse). L'uso del parametro pad aiuta a mitigarlo in alcuni casi, ma non sarà infallibile :)
  3. Aggiungerò collegamenti e riferimenti ai post originali dopo che avrò ripristinato il laptop (ho bisogno della cronologia)

PPS: Ho appena realizzato che è molto simile all'approccio suggerito da @DanMan e @ st.never. Verifica gli snippet di codice per un esempio di implementazione.


2

Soluzione javascript molto semplice. Divs deve essere in stile fe:

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

E 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

Non è una risposta esatta alla domanda, ma mi sono imbattuto in questa pagina quando ho provato a fare qualcosa di molto simile, ma volevo aggiungere un link per "visualizzare di più" piuttosto che solo una semplice ellissi. Questa è una funzione jQuery che aggiungerà un link "più" al testo che sta traboccando un contenitore. Personalmente lo sto usando con Bootstrap, ma ovviamente funzionerà senza.

Esempio di più screenshot

Per usare, metti il ​​testo in un contenitore come segue:

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

Quando viene aggiunta la seguente funzione jQuery, qualsiasi div che sia più grande del valore di regolazione dell'altezza verrà troncato e verrà aggiunto un link "Altro".

$(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);
        }
    });
});

Sulla base di questo, ma aggiornato: http://shakenandstirredweb.com/240/jquery-moreless-text


<sigh> Pensavo che qualcuno potesse sottovalutare questo, presumibilmente perché non è una risposta esatta alla domanda. Tuttavia, spero che qualcuno lo troverà utile, dato che non ho potuto trovare queste informazioni da nessun'altra parte ed è qui che ho finito dopo una ricerca.
Andy Beverley,

1

Il plug-in dotdotdot jQuery funziona bene con angolare:

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

Il markup corrispondente sarebbe:

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

1

Demo Pure JS (senza jQuery e loop 'while')

Quando ho cercato la soluzione del problema dell'ellissi multilinea, sono rimasto sorpreso dal fatto che non ci sia nulla di buono senza jQuery. Inoltre ci sono alcune soluzioni basate sul ciclo 'while', ma penso che non siano efficaci e pericolose a causa della possibilità di entrare in un ciclo infinito. Quindi ho scritto questo codice:

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

Forse abbastanza tardi ma usando SCSS puoi dichiarare una funzione come:

@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%
      );
    }
  }
}

E usalo come:

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

Che troncerà il testo su una riga e sapendo che è 1,4 la sua altezza. L'output previsto è il rendering di Chrome con ...alla fine e FF con qualche dissolvenza alla fine

Firefox

inserisci qui la descrizione dell'immagine

Cromo

inserisci qui la descrizione dell'immagine


1

Ho trovato questa breve soluzione solo CSS nella risposta di Adrien Be :

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

A partire dal marzo 2020 il supporto del browser è del 95,3% , non supportato in IE e Opera Mini. Funziona su Chrome, Safari, Firefox e Edge.


0

Probabilmente non puoi farlo (attualmente?) Senza un carattere a larghezza fissa come Courier. Con un carattere a larghezza fissa ogni lettera occupa lo stesso spazio orizzontale, quindi potresti probabilmente contare le lettere e moltiplicare il risultato con la dimensione del carattere corrente in em o ex. Quindi dovresti solo testare quante lettere si adattano su una riga e quindi romperle.

In alternativa, per i caratteri non fissi con potresti essere in grado di creare una mappatura per tutti i possibili caratteri (come i = 2px, m = 5px) e quindi fare i calcoli. Molto brutto lavoro però.


0

Per espandere la soluzione di @ DanMan: nel caso in cui vengano utilizzati caratteri a larghezza variabile, è possibile utilizzare una larghezza media dei caratteri. Ciò ha due problemi: 1) un testo con troppe W traboccerebbe e 2) un testo con troppi I verrebbe troncato prima.

Oppure potresti adottare un approccio nel caso peggiore e utilizzare la larghezza della lettera "W" (che credo sia la più ampia). Ciò elimina il problema 1 sopra ma intensifica il problema 2.

Un approccio diverso potrebbe essere: lasciare overflow: clipnel div e aggiungere una sezione di ellissi (forse un altro div o immagine) con float: right; position: relative; bottom: 0px;(non testato). Il trucco è far apparire l'immagine sopra la fine del testo.

Potresti anche visualizzare l'immagine solo quando sai che sta per traboccare (diciamo, dopo circa 100 caratteri)


Che cosa è overflow: clip? E cosa ti aspetteresti che floatfaccia quel CSS ?
Kapa,

0

Con questo codice non è necessario un div wrapper aggiuntivo se l'elemento ha un'altezza limitata da uno stile di altezza massima.

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

Inoltre, salva il testo originale in un attributo di dati che può essere visualizzato utilizzando solo stili, ad es. al passaggio del mouse sopra:

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

1
Questo è spesso un ciclo infinito.
Atadj,

0

Nel mio scenario non sono riuscito a far funzionare nessuna delle funzioni sopra menzionate e ho anche dovuto dire alla funzione quante linee mostrare indipendentemente dalla dimensione del carattere o dal contenitore.

Ho basato la mia soluzione sull'uso del metodo Canvas.measureText (che è una funzione HTML5 ) come spiegato qui da Domi , quindi non è completamente cross-browser.

Puoi vedere come funziona su questo violino .

Questo è il codice:

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

E l'HTML per usarlo sarebbe così:

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

Il codice per ottenere la famiglia di caratteri è piuttosto semplice e nel mio caso funziona, ma per scenari più complessi potrebbe essere necessario utilizzare qualcosa in questo senso .

Inoltre, nel mio caso, sto dicendo alla funzione quante linee usare, ma potresti calcolare quante linee mostrare in base alla dimensione e al carattere del contenitore.


0

Ho realizzato una versione che lascia intatto l'html. esempio 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;
}

esempio jsfiddle


0

Ho scritto un componente angolare che risolve il problema. Divide un determinato testo in elementi di span. Dopo il rendering, rimuove tutti gli elementi traboccanti e posiziona i puntini di sospensione subito dopo l'ultimo elemento visibile.

Esempio di utilizzo:

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

Demo Stackblitz: https://stackblitz.com/edit/angular-wfdqtd

Il componente:

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


-2

non sono sicuro se questo è quello che stai cercando, utilizza l'altezza minima anziché l'altezza.

    <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
Il problema è il numero 63 nei tuoi codici, se il numero è noto allora tutto diventa facile, solo una funzione troncata fa questo lavoro, come i tuoi codici. Tuttavia, come sapere il numero? In altre parole, come sapere dove verrà interrotto il testo? Se si può rispondere a questa domanda, il problema può essere semplicemente risolto nella logica di "1, calcolare il numero; 2, troncare"
Edward,

-3

Funzionerà molto semplice.

Direttiva:

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

Visualizza:

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

3
Come già discusso in altre risposte, il problema nel tuo codice è il numero 36. Oltre a rendere il codice specifico per una determinata larghezza del contenitore, non è anche accurato: con caratteri non a larghezza fissa, ci possono essere grandi differenze tra le lettere . Vedi iiiiiiiiiivs MMMMMMMMMM(con il carattere corrente non quello visibile però: D).
Kapa,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.