Sabit genişlik ve yükseklikte elips şeklinde çapraz tarayıcı çok satırlı metin taşması


178

Bu soru için anlaşılmasını kolaylaştırmak için bir resim hazırladım.

<div>Sabit genişlikte ve birden fazla çizgide bir üç nokta oluşturmak mümkün müdür ?

text-taşma

Orada ve orada bazı jQuery eklentileri denedim, ama aradığım birini bulamıyorum. Herhangi bir tavsiye? Fikirler?



1
ve stackoverflow.com/questions/3922739/… yalnızca css çözümü için
Evgeny


2
2016 ortalarında bunu arayan herkes için kısa cevap: HAYIR zarif, çapraz tarayıcı, CSS tek yolla mümkün değildir. Sıklıkla tamamlanmaya en yakın olarak verilen çözüm ( codepen.io/romanrudenko/pen/ymHFh ) o kadar Goldbergian ki tüm vücudunuzu incitir ve hala oldukça çirkin olur.
konrad

Yanıtlar:


91

Sadece hızlı bir temel fikir.

Aşağıdaki biçimlendirme ile test yapıyordum:

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

Ve CSS:

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

Bu jQuery uygulandığında istenen sonuç elde edilir:

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

İstenen boyuta ulaşana kadar metnin son kelimesini tekrar tekrar kaldırmaya çalışır. Taşma nedeniyle: gizli; süreç görünmez kalır ve JS kapalıyken bile sonuç "görsel olarak doğru" kalır (elbette "..." olmadan).

Eğer sunucu tarafında mantıklı bir kesme ile birleştirirseniz (bu sadece küçük bir ek yük bırakır) o zaman daha hızlı çalışır :).

Yine, bu tam bir çözüm değil, sadece bir fikir.

GÜNCELLEME: Bir jsFiddle Demosu eklendi .


1
@bazmegakapa harika bir çözüm ... ama benim durumuma uyarlamak için bazı sorunlar var. Çeşitli var live her birinin üzerinde bir .blockve a var .block h2ve bunu h2içeriye uygulamam gerekiyor .blockama işe yaramadım. Birden fazla olması farklı mıdır .block h2?
Alex

1
Benim durumumda 3 olması gerektiğinde sadece 2 satır metin bırakıyordu . Görünüşe göre kabım satırdan height*3birkaç piksel daha küçüktü . Kolay düzeltme sadece birkaç piksel eklemekdivh
Lukas LT

3
Metin sadece bir çok uzun kelime içerdiğinden, bu komut dosyasıyla sonsuz döngüde kötü bir deneyim yaşadım , böylece regexp yerine asla eşleşmedi. Bundan kaçınmak için, şu kodu hemen sonra ekleyin while:if(!$p.text().match(/\W*\s(\S)*$/)) break;
KrisWebDev

1
Bu durumda bir sorun olması muhtemel olmasa da, DOM'u güncellemek ve düzeni tekrar tekrar kontrol etmek kötü bir fikirdir, çünkü yavaşlamaya neden olabilir. Bunu azaltmak için, ikili aramaya benzer bir şey yapabilirsiniz: metin bloğunun zaten uyup uymadığını test edin, aksi takdirde metni kelimelere veya karakterlere bölün ve sınırlarınızı tanımlayın (alt = 1 kelime / karakter, üst = tüm kelimeler / karakter) , 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'in bulduğu gibi, dev bir kelimeyi de kontrol etmek isteyeceksiniz.
Chinoto Vokro

1
Bu çözüm harika. Benim durumumda, tam metin değerini duyarlı bir şekilde kesebilmem için orijinal metni takip etmem gerekiyor. Böylece sayfa yüklendiğinde, orijinal metni bir değişkende saklarım ve bu mantığı çalıştırmadan önce öğeyi orijinal metin değeriyle 'yenilediğimden emin olun. Debounce ekleyin ve harika çalışıyor.
besseddrest

68

JQuery.dotdotdot eklentisini deneyin .

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

11
Bunu nasıl telaffuz edersin? dot-dot-dot-nokta?
JackAce

58
Gerçekten css tarafından çözülmesi gereken bir sorunu çözmek için> 600 satır js kullanmak için berbat
Jethro Larson

Ben denedim, ve iyi çalışıyor. Kabul edilen cevap olmalı
AbdelHady

1
Çalışır, ancak yazı tipleri ve diğer harici kaynaklar HTML'nizin düzenini etkileyebileceği için $ (document) .ready () yerine window.loaded olayını kullandığınızdan emin olun. Dotdotdot bu kaynaklar yüklenmeden önce yürütülürse, metin doğru konumda kesilmez.
sboisse

10
Bu ticari bir araçtır, tek bir site için 5 dolar ve birden fazla site için 35 dolar. Lisanslama bir acı olur. Ben özgür ve hemen entegre edilebilir olduğunu düşündüm, öyle değil!
gen b.

29

"Çizgi sıkıştırma" için Javascript kütüphaneleri

"Çizgi kenetleme", "Çoklu çizgi bloğunda üç nokta" veya "dikey üç nokta" olarak da anılır.


github.com/BeSite/jQuery.dotdotdot


github.com/josephschmitt/Clamp.js


İşte henüz araştırmadım birkaç tane daha:


Hat kelepçesi için CSS çözümleri

Bazı CSS çözümleri var, ancak -webkit-line-clamptarayıcı desteği zayıf olan en basit kullanımlar . Canlı demo jsfiddle.net/AdrienBe/jthu55v7/

Birçok insan bunun sadece CSS kullanarak gerçekleşmesi için büyük çaba harcadı. Bununla ilgili makalelere ve sorulara bakın:


Ne öneririm

Basit tutun. Bu özelliğe ayırmak için çok fazla zamanınız yoksa, en basit ve test edilmiş çözümü seçin: basit CSS veya iyi test edilmiş bir javascript kütüphanesi.

Süslü / karmaşık / son derece özelleştirilmiş bir şey için gidin ve bu yolda fiyat ödeyecek.


Diğerleri ne yapar

Airbnb gibi solmaya iyi bir çözüm olabilir. Muhtemelen temel jQuery ile birleştirilmiş temel CSS'dir. Aslında, CSSTricks'teki bu çözüme çok benziyor

AirBnb "daha fazla bilgi edinin" çözümü

Oh, ve tasarım ilhamları arıyorsanız:


6

HTML'de böyle bir özellik yoktur ve bu çok sinir bozucudur.

Bununla başa çıkmak için bir kütüphane geliştirdim .

  • Çok satırlı metin taşması: üç nokta
  • Desteklemeyen teknolojilere sahip çok satırlı metin: SVG, Canvas
  • SVG metninizde, HTML oluşturma işleminizde ve PDF dışa aktarma işleminizde tam olarak aynı satır sonlarına sahip olun

Ekran görüntüsü, öğretici ve yükleme bağlantısı için siteme göz atın .


"db bağlantısı kurulurken hata" ... herkes gibi yapmak ve projenizi Github'da barındırmak isteyebilirsiniz, muhtemelen sizin ve toplum için daha iyi olurdu :)
Adrien Be

@AdrienBe Github'da: github.com/rossille/jstext ve haklısın, github web sitemden daha kararlı, github sayfasını ana bağlantı olarak ayarladım
Samuel Rossille

@SamuelRossille harika bir haber, hızlı güncelleme için teşekkürler!
Adrien Be

4

Bažmegakapa'nın çözümüne dayanan saf JS çözümü ve elemanın çizgisinden daha az bir yükseklik / maksimum yükseklik vermeye çalışan insanlar için bir miktar temizlik.

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

Bu çok etkisiz bir kod. Bu arada "while" görünümünü kullanmak sonsuz döngüde potansiyel bir hatadır.
WebBrother

4

İyi çalışan bir çözüm var ama bunun yerine bir elips degrade kullanır. Avantajları, herhangi bir JavaScript hesaplaması yapmak zorunda olmamanız ve tablo hücreleri de dahil olmak üzere değişken genişlikli kaplar için çalışmasıdır. Birkaç ekstra div kullanıyor, ancak uygulanması çok kolay.

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

Edit: Üzgünüz, bağlantının yeterli olmadığını bilmiyordum. Çözüm metnin etrafına bir div koymak ve taşmayı kontrol etmek için div'a stil uygulamaktır. Div içinde CSS veya bir görüntü (eski IE için) kullanılarak yapılabilen bir "solmaya" gradyanlı başka bir div koyun. Degrade, saydamdan tablo hücresinin arka plan rengine gider ve bir üç noktadan biraz daha geniştir. Metin uzunsa ve taşarsa, "solmaya" div altına girer ve "soluk" görünür. Metin kısaysa, solma görünmezdir, bu yüzden sorun olmaz. İki kap, kabın yüksekliğini metin satır yüksekliğinin bir katı olarak ayarlayarak bir veya daha fazla satırın gösterilmesine izin verecek şekilde ayarlanabilir. "Fade" div yalnızca son satırı kapsayacak şekilde konumlandırılabilir.


Lütfen çözümünüzün önemli kısımlarını paylaşın, SO bağlantısında sadece cevaplara izin verilmez.
kapa

bunun parlak yönü metnin kendisinin kesilmemesidir, bu nedenle kullanıcı tabloyu kopyalarsa tüm içerik görünür.
prototip

Çok güzel bir konsept. Ayrıca bu makalede bahsedilmiştir ("solmaya" yolu) Ben inanıyorum css-tricks.com/line-clampin
Adrien Be

4

Bunu başarmanın saf bir CSS yolu: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/

İşte bir özet:

resim açıklamasını buraya girin

<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

Sen kullanabilirsiniz -webkit-line-clampile mülk div.

-webkit-line-clamp: <integer>yani, içeriği kesmeden önce maksimum satır sayısını ayarlamak (…)ve son satırın sonunda bir üç nokta görüntüler .

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
Birinin bu cevabı neden reddettiğinden emin değilim. Mart 2020 itibarıyla tarayıcı desteği oldukça iyi -% 95 caniuse.com/#search=line-clamp
Yulian

2

İşte bir tutam kullanabileceğiniz bir vanilya JavaScript çözümü:

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

Aşağıdaki kod alanında onunla oynayabilirsiniz. CSS panelindeki yazı tipi boyutunu değiştirmeyi deneyin ve sonuçları güncellemek için HTML panelinde küçük bir düzenleme yapın (örneğin, bir yere fazladan boşluk ekleyin). Yazı tipi boyutundan bağımsız olarak, orta paragraf her zaman limitLines () öğesine iletilen ikinci parametrede satır sayısına kesilmelidir.

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


2

DÜZENLEME: Verilen Tıraş yüksekliğine dayalı çok satırlı metin kesme gerçekten çok iyi JS eklentisi olan Shave genelinde geldi . Optimum kırılma noktasını bulmak için ikili arama kullanır. Kesinlikle araştırmaya değer.


ORİJİNAL CEVAP:

Bu sorun için bir vanilya JS çözümü bulmak zorunda kaldı. Üzerinde çalıştığım durumda, sınırlı bir genişliğe ve iki satıra uzun bir ürün adı sığdırmak zorunda kaldım; Gerekirse üç nokta ile kesilir.

İhtiyaçlarıma uygun bir şey pişirmek için çeşitli SO yazılarının cevaplarını kullandım. Strateji aşağıdaki gibidir:

  1. İstenen yazı tipi boyutu için yazı tipi değişkeninin ortalama karakter genişliğini hesaplayın.
  2. Kabın genişliğini hesaplayın
  3. Kaptaki bir satıra sığacak karakter sayısını hesaplama
  4. Dizeye sığacak karakter sayısını ve metnin üzerine sarması gereken satır sayısını temel alarak dizeyi kesecek karakter sayısını hesaplayın.
  5. Giriş metnini bir önceki hesaplamaya (üç nokta tarafından eklenen ekstra karakterler için çarpanlara ayırma) göre kesin ve sonuna "..." ekleyin

Kod örneği:

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

Not:

  1. Kesim yalnızca bir satırda olacaksa, metin taşmasını kullanmak için saf CSS yöntemi: üç nokta daha temizdir
  2. Sabit genişliği olmayan yazı tipleri, kesmenin çok erken veya çok geç olmasına neden olabilir (farklı karakterler farklı genişliklere sahip olduğundan). Pad parametresini kullanmak bazı durumlarda bunu hafifletmeye yardımcı olur, ancak aptal olmayacaktır :)
  3. Dizüstü bilgisayar geri aldıktan sonra orijinal yayınlara bağlantılar ve referanslar ekleyeceğim (geçmişe ihtiyacım var)

PPS: Bunun @DanMan ve @ st.never tarafından önerilen yaklaşıma çok benzediğini fark ettim. Uygulama örneği için kod snippet'lerini kontrol edin.


2

Çok basit javascript çözümü. Divs şu ​​tarzda olmalıdır:

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

Ve 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

Soruya kesin bir cevap değil, ama ben çok benzer yapmaya çalışırken bu sayfaya rastladım, ama sadece basit bir üç nokta yerine "daha fazla görüntüle" için bir bağlantı eklemek istiyorum. Bu, bir kaptan taşan metne "daha" bağlantısı ekleyecek bir jQuery işlevidir. Şahsen bunu Bootstrap ile kullanıyorum, ama tabii ki onsuz çalışacak.

Örnek daha fazla ekran görüntüsü

Kullanmak için metninizi aşağıdaki gibi bir kaba koyun:

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

Aşağıdaki jQuery işlevi eklendiğinde, adjustheight değerinden daha büyük olan div'lardan herhangi biri kesilir ve bir "More" bağlantısı eklenir.

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

Buna dayanarak, ancak güncellendi: http://shakenandstirredweb.com/240/jquery-moreless-text


<sigh> Birisinin bunu muhtemelen küçümseyebileceğini düşündüm, çünkü bu soruya kesin bir cevap değil. Yine de, bu bilgiyi başka hiçbir yerde bulamadığım için birisinin yararlı bulacağını umuyorum ve burası bir aramadan sonra sona erdiğim yer.
Andy Beverley

1

Bahsedilen dotdotdot jQuery eklentisi açısal ile iyi çalışır:

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

Karşılık gelen işaretleme:

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

1

Pure JS demosu (jQuery ve 'while' döngüsü olmadan)

Çok satırlı elips probleminin çözümünü araştırdığımda jQuery olmadan iyi bir tane olmadığına şaşırdım. Ayrıca 'while' döngüsüne dayanan birkaç çözüm var, ancak sonsuz döngüye girme olasılığı nedeniyle etkili ve tehlikeli olmadıklarını düşünüyorum. Bu yüzden bu kodu yazdım:

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

Belki çok geç ama SCSS kullanarak aşağıdaki gibi bir işlev bildirebilirsiniz:

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

Ve şöyle kullanın:

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

Bu da metni bir satıra kesecek ve satır yüksekliği 1.4 olduğunu bilerek. Beklenen çıktı ..., sonunda renderleme için krom ve sonunda biraz solma ile FF

Firefox

resim açıklamasını buraya girin

Krom

resim açıklamasını buraya girin


1

Bu kısa CSS çözümünü Adrien Be'nin cevabında buldu :

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

İtibariyle Mart 2020 tarayıcı desteği olan % 95.3 değil IE ve Opera Mini destekleniyor. Chrome, Safari, Firefox ve Edge'de çalışır.


0

Courier gibi sabit genişlikli bir yazı tipi olmadan bunu yapamazsınız (şu anda?). Sabit genişlikli bir yazı tipiyle her harf aynı yatay alanı kaplar, böylece büyük olasılıkla harfleri sayabilir ve sonucu ems veya exs'deki geçerli yazı tipi boyutuyla çarpabilirsiniz. Daha sonra, bir satıra kaç harf sığdığını test etmeniz ve ardından ayırmanız gerekir.

Alternatif olarak, sabit olmayan yazı tipleri için olası tüm karakterler (i = 2px, m = 5px gibi) için bir eşleme oluşturabilir ve daha sonra matematiği yapabilirsiniz. Bir sürü çirkin iş olsa.


0

@ DanMan'ın çözümünü genişletmek için: değişken genişlikli yazı tiplerinin kullanılması durumunda, ortalama bir yazı tipi genişliği kullanabilirsiniz. Bunun iki sorunu vardır: 1) çok fazla W'ye sahip bir metin taşar ve 2) çok fazla I'ye sahip bir metin daha önce kesilir.

Veya en kötü durum yaklaşımını alabilir ve "W" harfinin (en geniş olduğuna inanıyorum) genişliğini kullanabilirsiniz. Bu, yukarıdaki sorun 1'i kaldırır, ancak sorun 2'yi yoğunlaştırır.

Farklı bir yaklaşım şöyle olabilir: div'de bırakın overflow: clipve float: right; position: relative; bottom: 0px;(denenmemiş) ile üç nokta (belki başka bir div veya resim) ekleyin . İşin püf noktası, görüntünün metnin sonunun üzerinde görünmesini sağlamaktır.

Görüntüyü yalnızca taşacağını bildiğinizde görüntüleyebilirsiniz (örneğin, yaklaşık 100 karakterden sonra)


Nedir overflow: clip? Ve bu CSS'nin ne floatyapmasını beklersiniz ?
kapa

0

Bu kodla, elemanın yüksekliği maksimum yükseklik stiliyle sınırlıysa, ekstra bir sarmalayıcı div'a gerek yoktur.

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

Ayrıca, orijinal metni yalnızca stiller kullanılarak görüntülenebilen bir veri özelliğine kaydeder, örn. fareyle üzerine gelindiğinde:

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

1
Bu genellikle sonsuz döngüdür.
Atadj

0

Senaryomda yukarıda belirtilen işlevlerin hiçbirini çalıştıramadım ve ayrıca yazı tipi boyutu veya kap boyutu ne olursa olsun, işleve kaç satır gösterileceğini söylemem gerekiyordu.

Çözümümü burada Domi tarafından açıklandığı gibi Canvas.measureText yönteminin (ki bir HTML5 özelliğidir) kullanımına dayandım , bu yüzden tamamen çapraz tarayıcı değil.

Bu keman üzerinde nasıl çalıştığını görebilirsiniz .

Bu kod:

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

Ve kullanmak için HTML şöyle olurdu:

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

Yazı tipi ailesini almak için kod oldukça basit ve benim durumumda çalışıyor, ancak daha karmaşık senaryolar için bu satırlar boyunca bir şeyler kullanmanız gerekebilir .

Ayrıca, benim durumumda fonksiyona kaç satır kullanılacağını söylüyorum, ancak konteyner boyutuna ve yazı tipine göre kaç satır gösterileceğini hesaplayabilirsiniz.


0

Html bozulmamış bir sürüm yaptık. jsfiddle örneği

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 örneği


0

Sorunu çözen açısal bir bileşen yazdım. Belirli bir metni yayılma öğelerine ayırır. Oluşturma işleminden sonra, taşan tüm öğeleri kaldırır ve üç nokta görünür olan son öğenin hemen sonrasına yerleştirir.

Kullanım örneği:

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

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

Bileşen:

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

aradığınızın bu olup olmadığından emin değilseniz, yükseklik yerine minimum yükseklik kullanır.

    <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
Sorun kodlarınızdaki 63 sayısıdır, eğer sayı biliniyorsa, o zaman her şey kolaylaşır, kodları olarak sadece bir kesme işlevi bu işi yapar. Ancak, sayı nasıl bilinir? Başka bir deyişle, metnin nereden satır kesileceğini nasıl öğrenebilirim? Bu soru cevaplanabilir, o zaman sorun "1, sayıyı hesaplamak; 2, kısaltmak" mantığı ile çözülebilir
Edward

-3

Çok basit bir işlev yapacak.

Direktif:

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

Görünüm:

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

3
Diğer yanıtlar altında daha önce tartıştığımız gibi, kodunuzdaki sorun 36 sayısıdır. Kodunuzu belirli bir kap genişliğine özgü yapmaktan başka, doğru da değildir: sabit genişlikli yazı tipleriyle, harfler arasında büyük farklılıklar olabilir . Bkz. iiiiiiiiiiVs MMMMMMMMMM(geçerli yazı tipi göründüğü gibi değilse: D).
kapa
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.