Browserübergreifender mehrzeiliger Textüberlauf mit Auslassungspunkten innerhalb einer festgelegten Breite und Höhe


178

Ich habe ein Bild für diese Frage erstellt, um das Verständnis zu erleichtern.

Ist es möglich, eine Ellipse auf einer <div>mit einer festen Breite und mehreren Linien zu erstellen ?

Textüberlauf

Ich habe hier und da einige jQuery-Plugins ausprobiert, kann aber das gesuchte nicht finden. Irgendeine Empfehlung? Ideen?





2
Für alle, die Mitte 2016 danach suchen, lautet die kurze Antwort: NEIN, es ist nicht in eleganter, browserübergreifender, nur CSS-Weise möglich. Die Lösung, die oft als am nächsten an der Vollendung angegeben wird ( codepen.io/romanrudenko/pen/ymHFh ), ist so goldbergisch , dass Ihr ganzer Körper verletzt wird und immer noch ziemlich hässlich ist.
Konrad

Antworten:


91

Nur eine kurze Grundidee.

Ich habe mit folgendem Markup getestet:

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

Und CSS:

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

Durch Anwenden dieser jQuery wird das gewünschte Ergebnis erzielt:

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

Es wird wiederholt versucht, das letzte Wort des Textes zu entfernen, bis die gewünschte Größe erreicht ist. Wegen des Überlaufs: versteckt; Der Prozess bleibt unsichtbar und auch bei ausgeschaltetem JS bleibt das Ergebnis "visuell korrekt" (natürlich ohne das "...").

Wenn Sie dies mit einer sinnvollen Kürzung auf der Serverseite kombinieren (die nur einen geringen Overhead hinterlässt), wird es schneller ausgeführt :).

Auch dies ist keine vollständige Lösung, sondern nur eine Idee.

UPDATE: Eine jsFiddle-Demo wurde hinzugefügt .


1
tolle Lösung @bazmegakapa ... aber ich habe einige Probleme beim Versuch, es an meinen Fall anzupassen. Ich habe verschiedene liund innen auf jedem gibt es ein .blockund ein .block h2und ich muss dies auf das h2Innere anwenden, .blockaber ich konnte es nicht zum Laufen bringen. Ist es anders, wenn es mehr als eine gibt .block h2?
Alex

1
In meinem Fall blieben nur 2 Textzeilen übrig, wenn es 3 hätte geben sollen. Anscheinend war mein Container um height*3einige Pixel kleiner als die Zeile . Einfache Lösung ist, einfach ein paar Pixel hinzuzufügendivh
Lukas LT

3
Ich hatte eine schlechte Erfahrung mit Endlosschleifen mit diesem Skript, da der Text nur ein sehr langes Wort enthielt, so dass der reguläre Ersatzausdruck nie übereinstimmte. Um dies zu vermeiden, fügen Sie diesen Code direkt nach der whileZeile hinzu:if(!$p.text().match(/\W*\s(\S)*$/)) break;
KrisWebDev

1
Obwohl dies in diesem Fall wahrscheinlich kein Problem darstellt, ist es eine schlechte Idee, das DOM zu aktualisieren und das Layout wiederholt zu überprüfen, da dies zu einer Verlangsamung führen kann. Um dies zu mildern, könnten Sie etwas Ähnliches wie eine binäre Suche tun: Testen Sie, ob der Textblock bereits passt, teilen Sie den Text ansonsten in Wörter oder Zeichen auf und definieren Sie Ihre Grenzen (unter = 1 Wort / Zeichen, oben = alle Wörter / Zeichen) , 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;}}. Wie @KrisWebDev herausgefunden hat, möchten Sie auch nach einem riesigen Wort suchen.
Chinoto Vokro

1
Diese Lösung ist großartig. In meinem Fall muss ich den Originaltext verfolgen, damit ich den Volltextwert reaktionsschnell abschneiden kann. Wenn die Seite geladen wird, speichere ich den Originaltext in einer Variablen und bevor ich diese Logik ausführe, stelle ich sicher, dass das Element mit dem Originaltextwert aktualisiert wird. Fügen Sie Debounce hinzu, und es funktioniert wunderbar.
Besseddrest

68

Probieren Sie das Plugin jQuery.dotdotdot aus .

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

11
Wie würden Sie das aussprechen? Punkt-Punkt-Punkt-Punkt?
JackAce

58
Wirklich scheiße,> 600 Zeilen js zu verwenden, um ein Problem zu lösen, das von CSS gelöst werden sollte
Jethro Larson

Ich habe es versucht und es funktioniert gut. Sollte die akzeptierte Antwort sein
AbdelHady

1
Funktioniert, aber stellen Sie sicher, dass Sie das Ereignis window.loaded und nicht $ (document) .ready () verwenden, da Schriftarten und andere externe Ressourcen das Layout Ihres HTML-Codes beeinflussen können. Wenn dotdotdot ausgeführt wird, bevor diese Ressourcen geladen werden, wird der Text nicht an der richtigen Position abgeschnitten.
Sboisse

10
Dies ist ein kommerzielles Tool, das 5 US-Dollar für eine einzelne Site und 35 US-Dollar für mehrere Sites kostet. Die Lizenzierung wäre ein Schmerz. Ich fand es kostenlos und sofort integrierbar, nicht so!
Gen b.

29

Javascript-Bibliotheken für "Line Clamping"

Beachten Sie, dass "Linienklemmung" auch als "Ellipse auf Block mit mehreren Linien" oder "vertikale Ellipse" bezeichnet wird.


github.com/BeSite/jQuery.dotdotdot


github.com/josephschmitt/Clamp.js


Hier sind noch einige, die ich noch nicht untersucht habe:


CSS-Lösungen für die Linienklemmung

Es gibt einige CSS-Lösungen, aber die einfachste Verwendung -webkit-line-clampmit schlechter Browserunterstützung . Siehe Live-Demo auf jsfiddle.net/AdrienBe/jthu55v7/

Viele Menschen haben große Anstrengungen unternommen, um dies nur mit CSS zu erreichen. Siehe Artikel und Fragen dazu:


Was ich empfehlen würde

Halte es einfach. Entscheiden Sie sich für die einfachste und getestete Lösung: einfaches CSS oder eine gut getestete Javascript-Bibliothek, es sei denn, Sie haben viel Zeit, um sich dieser Funktion zu widmen.

Entscheiden Sie sich für etwas Schickes / Komplexes / Hochindividuelles und Sie werden den Preis dafür später zahlen.


Was andere tun

Ein Ausblenden wie bei Airbnb könnte eine gute Lösung sein. Es handelt sich wahrscheinlich um einfaches CSS in Verbindung mit einfachem jQuery. Eigentlich scheint es dieser Lösung auf CSSTricks sehr ähnlich zu sein

AirBnb "read more" -Lösung

Oh, und wenn Sie nach Designinspirationen suchen:


6

In HTML gibt es keine solche Funktion, und das ist sehr frustrierend.

Ich habe eine Bibliothek entwickelt, um damit umzugehen.

  • Mehrzeiliger Textüberlauf: Auslassungspunkte
  • Mehrzeiliger Text mit Technologien, die ihn nicht unterstützen: SVG, Canvas zum Beispiel
  • Haben Sie genau die gleichen Zeilenumbrüche in Ihrem SVG-Text, in Ihrem HTML-Rendering und beispielsweise in Ihrem PDF-Export

Auf meiner Website finden Sie Screenshots, Tutorials und Download-Links.


"Fehler beim Herstellen der Datenbankverbindung" ... Sie möchten vielleicht wie alle anderen tun und Ihr Projekt auf Github hosten, es wäre wahrscheinlich schöner für Sie und für die Community :)
Adrien Be

@AdrienBe es ist auf Github: github.com/rossille/jstext , und Sie haben Recht, da Github stabiler als meine Website ist, habe ich die Github-Seite als Hauptlink festgelegt
Samuel Rossille

@SamuelRossille tolle Neuigkeiten, danke für das schnelle Update!
Adrien Be

4

Reine JS-Lösung basierend auf der Lösung von bažmegakapa und einige Bereinigungen, um Personen zu berücksichtigen, die versuchen, eine Höhe / maximale Höhe anzugeben, die kleiner als die Linie des Elements ist.

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

Dies ist ein sehr unwirksamer Code. Übrigens ist die Verwendung von "while" -Look ein potenzieller Fehler bei der Endlosschleife.
WebBrother

4

Ich habe eine Lösung, die gut funktioniert, aber stattdessen verwendet eine Ellipse einen Farbverlauf. Die Vorteile sind, dass Sie keine JavaScript-Berechnungen durchführen müssen und dies für Container mit variabler Breite einschließlich Tabellenzellen funktioniert. Es verwendet ein paar zusätzliche Divs, ist aber sehr einfach zu implementieren.

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

Edit: Sorry, ich wusste nicht, dass der Link nicht genug ist. Die Lösung besteht darin, ein Div um den Text zu setzen und das Div so zu gestalten, dass der Überlauf gesteuert wird. Fügen Sie innerhalb des Div ein weiteres Div mit einem "Fade" -Gradienten ein, der mithilfe von CSS oder einem Bild (für den alten IE) erstellt werden kann. Der Farbverlauf reicht von transparent bis zur Hintergrundfarbe der Tabellenzelle und ist etwas breiter als eine Ellipse. Wenn der Text lang ist und überläuft, wird er unter dem Div "Fade" angezeigt und sieht "Faded Out" aus. Wenn der Text kurz ist, ist die Überblendung unsichtbar, sodass kein Problem besteht. Die beiden Container können so angepasst werden, dass eine oder mehrere Zeilen angezeigt werden, indem die Höhe des Containers als Vielfaches der Textzeilenhöhe festgelegt wird. Das "Fade" -Div kann so positioniert werden, dass es nur die letzte Zeile abdeckt.


Bitte teilen Sie die wichtigen Teile Ihrer Lösung mit, auf SO sind nur Linkantworten nicht zulässig.
Kapa

Der brillante Aspekt dabei ist, dass der Text selbst nicht abgeschnitten wird. Wenn der Benutzer die Tabelle kopiert, wird der gesamte Inhalt angezeigt.
Prototyp

Ein sehr schönes Konzept. Es wird auch in diesem Artikel erwähnt ("Ausblenden" Weg) Ich glaube, css-tricks.com/line-clampin
Adrien Be

4

Hier ist eine reine CSS-Methode, um dies zu erreichen: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/

Hier ist eine Zusammenfassung:

Geben Sie hier die Bildbeschreibung ein

<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

Sie können -webkit-line-clampEigenschaft mit verwenden div.

-webkit-line-clamp: <integer>Dies bedeutet, dass Sie die maximale Anzahl von Zeilen festlegen, bevor Sie den Inhalt abschneiden, und (…)am Ende der letzten Zeile eine Ellipse anzeigen .

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
Ich bin mir nicht sicher, warum jemand diese Antwort abgelehnt hat. Die Browserunterstützung ab März 2020 ist ziemlich anständig - 95% caniuse.com/#search=line-clamp
Yulian

2

Hier ist eine Vanille-JavaScript-Lösung, die Sie zur Not verwenden können:

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

Sie können damit im Codepen unten herumspielen. Versuchen Sie, die Schriftgröße im CSS-Bereich zu ändern, und nehmen Sie im HTML-Bereich eine geringfügige Änderung vor (z. B. fügen Sie irgendwo einen zusätzlichen Platz hinzu), um die Ergebnisse zu aktualisieren. Unabhängig von der Schriftgröße sollte der mittlere Absatz immer auf die Anzahl der Zeilen im zweiten Parameter gekürzt werden, der an limitLines () übergeben wird.

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


2

BEARBEITEN: Kam über Shave, das JS-Plugin, das das mehrzeilige Abschneiden von Text basierend auf einer bestimmten maximalen Höhe sehr gut macht. Es verwendet die binäre Suche, um den optimalen Haltepunkt zu finden. Auf jeden Fall eine Untersuchung wert.


URSPRÜNGLICHE ANTWORT:

Ich musste eine Vanille-JS-Lösung für dieses Problem finden. In dem Fall, an dem ich gearbeitet hatte, musste ich einen langen Produktnamen in eine begrenzte Breite und über zwei Zeilen einpassen. bei Bedarf durch Auslassungspunkte abgeschnitten.

Ich habe Antworten aus verschiedenen SO-Posts verwendet, um etwas zu erfinden, das meinen Bedürfnissen entspricht. Die Strategie lautet wie folgt:

  1. Berechnen Sie die durchschnittliche Zeichenbreite der Schriftvariante für die gewünschte Schriftgröße.
  2. Berechnen Sie die Breite des Containers
  3. Berechnen Sie die Anzahl der Zeichen, die in eine Zeile im Container passen
  4. Berechnen Sie die Anzahl der Zeichen, auf die die Zeichenfolge gekürzt werden soll, basierend auf der Anzahl der Zeichen, die in eine Zeile passen, und der Anzahl der Zeilen, die der Text umbrechen soll.
  5. Schneiden Sie den Eingabetext basierend auf der vorherigen Berechnung ab (unter Berücksichtigung der durch Auslassungspunkte hinzugefügten zusätzlichen Zeichen) und fügen Sie am Ende "..." hinzu

Codebeispiel:

/**
 * 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. Wenn die Kürzung nur in einer Zeile erfolgen soll, ist die reine CSS-Methode zur Verwendung von Textüberlauf: Ellipse übersichtlicher
  2. Schriftarten ohne feste Breite können dazu führen, dass die Kürzung zu früh oder zu spät erfolgt (da unterschiedliche Zeichen unterschiedliche Breiten haben). Die Verwendung des Pad-Parameters hilft in einigen Fällen, dies zu mildern, ist jedoch nicht narrensicher :)
  3. Fügt Links und Verweise zu den Originalbeiträgen hinzu, nachdem ich den Laptop zurückerhalten habe (benötige Verlauf)

PPS: Ich habe gerade festgestellt, dass dies dem von @DanMan und @ st.never vorgeschlagenen Ansatz sehr ähnlich ist. Überprüfen Sie die Codefragmente auf ein Implementierungsbeispiel.


2

Sehr einfache Javascript-Lösung. Divs muss gestylt werden, z.

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

Und 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

Keine genaue Antwort auf die Frage, aber ich bin auf diese Seite gestoßen, als ich versucht habe, etwas sehr Ähnliches zu tun, aber einen Link zum "Mehr anzeigen" hinzufügen wollte, anstatt nur eine einfache Ellipse. Dies ist eine jQuery-Funktion, die einen "Mehr" -Link zu Text hinzufügt, der einen Container überläuft. Persönlich benutze ich dies mit Bootstrap, aber natürlich wird es ohne funktionieren.

Beispiel mehr Screenshot

Legen Sie Ihren Text wie folgt in einen Container:

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

Wenn die folgende jQuery-Funktion hinzugefügt wird, werden alle Divs, die größer als der Wert für die Anpassungshöhe sind, abgeschnitten und ein Link "Mehr" hinzugefügt.

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

Basierend darauf, aber aktualisiert: http://shakenandstirredweb.com/240/jquery-moreless-text


<seufz> Ich dachte, jemand könnte dies ablehnen, vermutlich weil es keine genaue Antwort auf die Frage ist. Trotzdem hoffe ich, dass jemand es nützlich findet, da ich diese Informationen nirgendwo anders finden konnte und hier nach einer Suche endete.
Andy Beverley

1

Das erwähnte dotdotdot jQuery Plugin funktioniert gut mit eckigen:

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

Das entsprechende Markup wäre:

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

1

Reine JS-Demo (ohne jQuery und 'while'-Schleife)

Als ich nach einer Lösung für ein mehrzeiliges Ellipsenproblem suchte, war ich überrascht, dass es ohne jQuery keine gute gibt. Es gibt auch einige Lösungen, die auf der 'while'-Schleife basieren, aber ich denke, sie sind nicht effektiv und gefährlich, da die Möglichkeit besteht, in die Endlosschleife zu gelangen. Also habe ich diesen Code geschrieben:

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

Vielleicht ziemlich spät, aber mit SCSS können Sie eine Funktion deklarieren wie:

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

Und benutze es wie:

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

Dadurch wird der Text auf eine Zeile gekürzt und Sie wissen, dass er 1,4 Zeilen hoch ist. Die erwartete Ausgabe ist Chrom, mit dem ...am Ende gerendert werden soll, und FF mit etwas kühlem Fade am Ende

Feuerfuchs

Geben Sie hier die Bildbeschreibung ein

Chrom

Geben Sie hier die Bildbeschreibung ein


1

Diese kurze CSS- Lösung wurde in Adrien Be's Antwort gefunden :

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

Ab März 2020 beträgt die Browserunterstützung 95,3% und wird in IE und Opera Mini nicht unterstützt. Funktioniert mit Chrome, Safari, Firefox und Edge.


0

Sie können es wahrscheinlich (derzeit?) Ohne eine Schriftart mit fester Breite wie Courier nicht tun. Bei einer Schriftart mit fester Breite nimmt jeder Buchstabe den gleichen horizontalen Raum ein, sodass Sie wahrscheinlich die Buchstaben zählen und das Ergebnis mit der aktuellen Schriftgröße in ems oder exs multiplizieren können. Dann müssten Sie nur noch testen, wie viele Buchstaben in eine Zeile passen, und sie dann aufteilen.

Alternativ können Sie für nicht festgeschriebene Schriftarten möglicherweise eine Zuordnung für alle möglichen Zeichen (wie i = 2px, m = 5px) erstellen und dann rechnen. Eine Menge hässlicher Arbeit.


0

So erweitern Sie die Lösung von @ DanMan: Wenn Schriftarten mit variabler Breite verwendet werden, können Sie eine durchschnittliche Schriftbreite verwenden. Dies hat zwei Probleme: 1) Ein Text mit zu vielen W würde überlaufen und 2) ein Text mit zu vielen I würde früher abgeschnitten.

Oder Sie könnten einen Worst-Case-Ansatz wählen und die Breite des Buchstabens "W" verwenden (was meiner Meinung nach am breitesten ist). Dies beseitigt das obige Problem 1, verschärft jedoch das Problem 2.

Ein anderer Ansatz könnte sein: Lassen Sie overflow: clipdas Div und fügen Sie einen Auslassungsabschnitt (möglicherweise ein anderes Div oder Bild) mit float: right; position: relative; bottom: 0px;(ungetestet) hinzu. Der Trick besteht darin, das Bild über dem Ende des Textes erscheinen zu lassen.

Sie können das Bild auch nur anzeigen, wenn Sie wissen, dass es überlaufen wird (z. B. nach etwa 100 Zeichen).


Was ist overflow: clip? Und was würden Sie von CSS erwarten float?
Kapa

0

Mit diesem Code ist kein zusätzliches Wrapper-Div erforderlich, wenn die Höhe des Elements durch einen Stil mit maximaler Höhe begrenzt ist.

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

Außerdem wird der Originaltext in einem Datenattribut gespeichert, das nur mit Stilen angezeigt werden kann, z. mit der Maus über:

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

1
Das ist oft eine Endlosschleife.
Atadj

0

In meinem Szenario konnte ich keine der oben genannten Funktionen ausführen, und ich musste der Funktion auch mitteilen, wie viele Zeilen angezeigt werden sollen, unabhängig von der Schriftgröße oder der Containergröße.

Ich habe meine Lösung auf die Verwendung der Canvas.measureText- Methode (eine HTML5- Funktion) gestützt, wie hier von Domi erläutert , sodass sie nicht vollständig browserübergreifend ist.

Sie können sehen, wie es auf dieser Geige funktioniert .

Dies ist der Code:

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

Und das HTML, um es zu verwenden, wäre wie folgt:

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

Der Code zum Abrufen der Schriftfamilie ist recht einfach und funktioniert in meinem Fall. In komplexeren Szenarien müssen Sie möglicherweise etwas in diese Richtung verwenden .

In meinem Fall sage ich der Funktion auch, wie viele Zeilen verwendet werden sollen, aber Sie können berechnen, wie viele Zeilen entsprechend der Containergröße und der Schriftart angezeigt werden sollen.


0

Ich habe eine Version gemacht, die das HTML intakt lässt. jsfiddle Beispiel

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 Beispiel


0

Ich habe eine Winkelkomponente geschrieben, die das Problem löst. Es teilt einen bestimmten Text in span-Elemente auf. Nach dem Rendern werden alle überlaufenden Elemente entfernt und die Auslassungspunkte direkt nach dem letzten sichtbaren Element platziert.

Anwendungsbeispiel:

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

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

Die Komponente:

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

Hier habe ich eine weitere Bibliothek mit schnellerem Algorithmus erstellt. Bitte prüfen:

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

So installieren Sie mit einer Laube:

bower install fast-ellipsis

So installieren Sie mit npm:

npm install fast-ellipsis 

Ich hoffe dir gefällt es!


-2

Ich bin mir nicht sicher, ob dies das ist, wonach Sie suchen. Es wird die Mindesthöhe anstelle der Höhe verwendet.

    <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
Das Problem ist die Nummer 63 in Ihren Codes. Wenn die Nummer bekannt ist, wird alles einfach. Nur eine Kürzungsfunktion erledigt diese Aufgabe, wie Ihre Codes. Wie kann man die Nummer kennen? Mit anderen Worten, woher wissen Sie, wo der Text in Zeilenumbrüchen angezeigt wird? Wenn diese Frage beantwortet werden kann, kann das Problem einfach in der Logik "1, berechne die Zahl; 2, abgeschnitten" gelöst werden
Edward

-3

Sehr einfache Funktion reicht aus.

Richtlinie:

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

Aussicht:

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

3
Wie wir bereits unter anderen Antworten besprochen haben, ist das Problem in Ihrem Code die Nummer 36. Abgesehen davon, dass Ihr Code für eine bestimmte Containerbreite spezifisch ist, ist er auch nicht genau: Bei Schriftarten mit nicht fester Breite kann es große Unterschiede zwischen Buchstaben geben . Siehe iiiiiiiiiivs MMMMMMMMMM(wobei die aktuelle Schriftart jedoch nicht so sichtbar ist: D).
Kapa
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.