Overflow teks multi-baris lintas-browser dengan elips ditambahkan dalam lebar dan tinggi tetap

178

Saya membuat gambar untuk pertanyaan ini agar lebih mudah dipahami.

Apakah mungkin membuat elipsis <div>dengan lebar tetap dan beberapa garis?

text-overflow

Saya sudah mencoba beberapa plugin jQuery di sana-sini, tetapi tidak dapat menemukan yang saya cari. Ada rekomendasi? Ide ide?

Edward
sumber
1
dan stackoverflow.com/questions/3922739/… untuk solusi khusus-css
Evgeny
artikel terkait css-tricks.com/line-clampin
Adrien Be
2
Bagi siapa pun yang mencari ini di pertengahan 2016, jawaban singkatnya adalah: TIDAK, itu tidak mungkin dalam browser yang elegan, lintas-satunya, CSS saja. Solusi yang sering diberikan sebagai yang paling dekat dengan penyelesaian ( codepen.io/romanrudenko/pen/ymHFh ) begitu Goldbergian itu membuat semua tubuh Anda sakit, dan masih cukup jelek.
konrad

Jawaban:

91

Hanya ide dasar yang cepat.

Saya sedang menguji dengan markup berikut:

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

Dan CSS:

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

Menerapkan jQuery ini akan mencapai hasil yang diinginkan:

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

Berulang kali mencoba menghapus kata terakhir dari teks hingga mencapai ukuran yang diinginkan. Karena overflow: tersembunyi; prosesnya tetap tidak terlihat dan bahkan dengan JS dimatikan hasilnya tetap 'benar secara visual' (tanpa "..." tentu saja).

Jika Anda menggabungkan ini dengan pemotongan yang masuk akal pada sisi-server (yang hanya menyisakan sedikit overhead) maka akan berjalan lebih cepat :).

Sekali lagi, ini bukan solusi lengkap, hanya sebuah ide.

UPDATE: Menambahkan Demo jsFiddle .

kapa
sumber
1
solusi hebat @bazmegakapa ... tapi saya punya beberapa masalah mencoba menyesuaikannya dengan kasus saya. Saya memiliki berbagai lidan di dalamnya masing-masing ada .blockdan .block h2dan saya perlu menerapkan ini ke h2dalam .blocktetapi saya tidak bisa membuatnya bekerja. Apakah ada perbedaan jika ada lebih dari satu .block h2?
Alex
1
Dalam kasus saya itu hanya menyisakan 2 baris teks ketika seharusnya ada 3. Rupanya wadah saya lebih kecil dari garis height*3dengan beberapa piksel. Perbaikan yang mudah adalah dengan hanya menambahkan beberapa piksel kedivh
Lukas LT
3
Saya memiliki pengalaman buruk tentang infinite loop dengan skrip ini karena teks hanya berisi satu kata yang sangat panjang, jadi ganti regexp tidak pernah cocok. Untuk menghindari ini, tambahkan kode ini tepat setelah whilebaris:if(!$p.text().match(/\W*\s(\S)*$/)) break;
KrisWebDev
1
Meskipun tidak mungkin menjadi masalah dalam hal ini, memperbarui DOM dan memeriksa tata letak berulang kali adalah ide yang buruk karena dapat menyebabkan pelambatan. Untuk mengurangi ini, Anda dapat melakukan sesuatu yang mirip dengan pencarian biner: tes untuk melihat apakah blok teks sudah cocok, jika tidak, pisahkan teks menjadi kata atau karakter dan tentukan batas Anda (lebih rendah = 1 kata / karakter, atas = semua kata / 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;}}. Seperti yang ditemukan oleh @KrisWebDev, Anda juga ingin memeriksa satu kata raksasa.
Chinoto Vokro
1
Solusi ini sangat bagus. Dalam kasus saya, saya perlu melacak teks asli sehingga saya dapat memotong nilai teks penuh secara responsif. Jadi ketika halaman dimuat, saya menyimpan teks asli dalam sebuah variabel, dan sebelum saya menjalankan logika ini saya pastikan untuk 'menyegarkan' elemen dengan nilai teks asli. Tambahkan debounce, dan itu berfungsi dengan sangat baik.
besseddrest
68

Coba plugin jQuery.dotdotdot .

$(".ellipsis").dotdotdot();
Mat
sumber
11
Bagaimana Anda mengucapkannya? titik titik titik titik?
JackAce
58
Benar-benar menyebalkan untuk menggunakan> 600 baris js untuk menyelesaikan masalah yang harus diselesaikan oleh css
Jethro Larson
Saya sudah mencobanya, dan berfungsi baik. Seharusnya jawaban yang diterima
AbdelHady
1
Berhasil tetapi pastikan untuk menggunakan event window.loaded dan bukan $ (document) .ready (), karena font dan sumber daya eksternal lainnya dapat memengaruhi tata letak HTML Anda. Jika dotdotdot dijalankan sebelum sumber daya ini dimuat, teks tidak akan terpotong pada posisi yang benar.
sboisse
10
Ini adalah alat komersial, harganya $ 5 untuk satu situs dan $ 35 untuk beberapa situs. Lisensi akan menyusahkan. Saya pikir itu gratis dan segera dapat diintegrasikan, tidak begitu!
gen b.
29

Perpustakaan Javascript untuk "line clamping"

Perhatikan bahwa "penjepitan garis" juga disebut sebagai "Ellipsis pada blok multi-garis" atau "elipsis vertikal".


github.com/BeSite/jQuery.dotdotdot


github.com/josephschmitt/Clamp.js


Berikut adalah beberapa lagi yang belum saya selidiki:


Solusi CSS untuk penjepitan garis

Ada beberapa solusi CSS, tetapi penggunaan paling sederhana -webkit-line-clampyang memiliki dukungan browser yang buruk . Lihat demo langsung di jsfiddle.net/AdrienBe/jthu55v7/

Banyak orang berusaha keras untuk mewujudkan hal ini menggunakan CSS saja. Lihat artikel dan pertanyaan tentang itu:


Apa yang saya rekomendasikan

Tetap sederhana. Kecuali Anda memiliki banyak waktu untuk mendedikasikan fitur ini, gunakan solusi yang paling sederhana & teruji: CSS sederhana atau perpustakaan javascript yang teruji dengan baik.

Pergi untuk sesuatu yang mewah / kompleks / sangat disesuaikan & Anda akan membayar harga untuk ini di jalan.


Apa yang orang lain lakukan

Memudar seperti Airbnb mungkin merupakan solusi yang bagus. Mungkin itu adalah CSS dasar ditambah dengan jQuery dasar. Sebenarnya, sepertinya sangat mirip dengan solusi ini di CSSTricks

Solusi "baca lebih lanjut" AirBnb

Oh, dan jika Anda mencari inspirasi desain:

Adrien Be
sumber
6

Tidak ada fitur seperti itu di HTML, dan ini sangat menyebalkan.

Saya telah mengembangkan perpustakaan untuk menangani hal ini.

  • Multiline text-overflow: ellipsis
  • Teks multiline dengan teknologi yang tidak mendukungnya: SVG, Canvas misalnya
  • Memiliki jeda baris yang sama persis dalam teks SVG Anda, dalam rendering HTML Anda, dan dalam ekspor PDF Anda misalnya

Lihat situs saya untuk tangkapan layar, tutorial, dan tautan dowload.

Samuel Rossille
sumber
"kesalahan membangun koneksi db" ... Anda mungkin ingin melakukan seperti orang lain & meng-host proyek Anda di Github, mungkin akan lebih baik untuk Anda & untuk komunitas :)
Adrien Be
@AdrienBe ada di Github: github.com/rossille/jstext , dan Anda benar, github lebih stabil daripada situs web saya, saya mengatur halaman github sebagai tautan utama
Samuel Rossille
@SamuelRossille berita bagus, terima kasih atas pembaruan cepatnya!
Adrien Be
4

Solusi JS murni berdasarkan solusi bažmegakapa, dan beberapa pembersihan untuk memperhitungkan orang-orang yang mencoba memberikan tinggi / maks-tinggi yang kurang dari garis elemenTinggi:

  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);
prashn64
sumber
Ini adalah kode yang sangat tidak efektif. Ngomong-ngomong menggunakan tampilan "while" adalah bug potensial dengan infinite loop.
WebBrother
4

Saya punya solusi yang berfungsi dengan baik tetapi elipsis ia menggunakan gradien. Keuntungannya adalah Anda tidak perlu melakukan perhitungan JavaScript apa pun dan berfungsi untuk wadah lebar variabel termasuk sel tabel. Ini menggunakan beberapa div tambahan, tetapi sangat mudah diimplementasikan.

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

Sunting: Maaf, saya tidak tahu bahwa tautannya tidak cukup. Solusinya adalah dengan meletakkan div di sekitar teks, dan gaya div untuk mengendalikan overflow. Di dalam div, letakkan div lain dengan gradien "fade" yang dapat dibuat dengan menggunakan CSS atau gambar (untuk IE lama). Gradien berubah dari transparan ke warna latar belakang sel tabel dan sedikit lebih lebar dari elipsis. Jika teksnya panjang dan meluap, ia berada di bawah "fade" div dan terlihat "faded out". Jika teks pendek, fade tidak terlihat sehingga tidak ada masalah. Dua kontainer dapat disesuaikan untuk membiarkan satu atau beberapa baris ditampilkan dengan mengatur ketinggian wadah sebagai kelipatan dari tinggi baris teks. Div "fade" hanya dapat diposisikan untuk menutupi baris terakhir.

Miriam Salzer
sumber
Silakan bagikan bagian penting dari solusi Anda, pada jawaban hanya tautan SO tidak diizinkan.
kapa
Aspek cemerlang dari ini adalah teks itu sendiri tidak terpotong, jadi jika pengguna menyalin-paste tabel seluruh konten muncul.
prototipe
Konsep yang sangat bagus. Hal ini juga disebutkan dalam artikel ini ("fade out" way) Saya percaya css-tricks.com/line-clampin
Adrien Be
4

Berikut ini adalah cara CSS murni untuk mencapai ini: http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/

Berikut ini ringkasannya:

masukkan deskripsi gambar di sini

<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>
Aaron Hoffman
sumber
4

Anda dapat menggunakan -webkit-line-clampproperti dengan div.

-webkit-line-clamp: <integer>yang berarti mengatur jumlah maksimum baris sebelum memotong konten dan kemudian menampilkan elipsis (…)di akhir baris terakhir.

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>

Penny Liu
sumber
2
Tidak yakin mengapa seseorang menurunkan jawaban ini. Dukungan browser pada Maret 2020 cukup baik - 95% caniuse.com/#search=line-clamp
Yulian
2

Inilah solusi JavaScript vanilla yang dapat Anda gunakan dalam keadaan darurat:

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

Anda dapat bermain-main dengannya di codepen di bawah ini. Coba ubah ukuran font di panel CSS dan lakukan sedikit pengeditan di panel HTML (mis., Tambahkan ruang tambahan di suatu tempat) untuk memperbarui hasilnya. Terlepas dari ukuran font, paragraf tengah harus selalu dipotong ke jumlah baris pada parameter kedua yang diteruskan ke limitLines ().

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

Terima kasih
sumber
2

EDIT: Datang di Shave yang merupakan plugin JS yang melakukan pemotongan teks multi baris berdasarkan max-height yang diberikan dengan sangat baik. Ini menggunakan pencarian biner untuk menemukan titik istirahat optimal. Pasti layak diselidiki.


JAWABAN ASLI:

Saya harus datang dengan solusi vanilla JS untuk masalah ini. Dalam hal saya telah mengerjakan, saya harus memasukkan nama produk yang panjang menjadi lebar terbatas dan lebih dari dua baris; terpotong oleh elipsis jika diperlukan.

Saya menggunakan jawaban dari berbagai posting SO untuk memasak sesuatu yang sesuai dengan kebutuhan saya. Strateginya adalah sebagai berikut:

  1. Hitung rata-rata lebar karakter varian font untuk ukuran font yang diinginkan.
  2. Hitung lebar wadah
  3. Hitung jumlah karakter yang sesuai pada satu baris dalam wadah
  4. Hitung jumlah karakter untuk memotong string berdasarkan jumlah karakter yang sesuai pada garis dan jumlah garis teks seharusnya membungkus.
  5. Pangkas teks input berdasarkan perhitungan sebelumnya (anjak lebih dalam untuk karakter tambahan ditambahkan oleh elipsis) dan tambahkan "..." ke akhir

Contoh kode:

/**
 * 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. Jika pemotongan hanya pada satu baris, metode CSS murni menggunakan text-overflow: ellipsis lebih rapi
  2. Font yang tidak memiliki lebar tetap dapat menyebabkan pemotongan terjadi terlalu dini atau terlalu terlambat (karena karakter yang berbeda memiliki lebar yang berbeda). Menggunakan parameter pad membantu mengurangi ini dalam beberapa kasus tetapi tidak akan menjadi bukti bodoh :)
  3. Akan menambahkan tautan dan referensi ke posting asli setelah saya mendapatkan laptop kembali (perlu riwayat)

PPS: Baru menyadari ini sangat mirip dengan pendekatan seperti yang disarankan oleh @DanMan dan @ st.never. Lihat cuplikan kode untuk contoh implementasi.

Chirag Ravindra
sumber
2

Solusi javascript yang sangat sederhana. Div harus ditata:

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

Dan 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 + "…";
  }
}
Michal Politzer
sumber
1

Bukan jawaban yang tepat untuk pertanyaan itu, tetapi saya menemukan halaman ini ketika mencoba melakukan hal yang sangat mirip, tetapi ingin menambahkan tautan ke "view more" daripada hanya ellipsis yang langsung. Ini adalah fungsi jQuery yang akan menambahkan tautan "lebih" ke teks yang meluap wadah. Secara pribadi saya menggunakan ini dengan Bootstrap, tetapi tentu saja itu akan berhasil tanpa.

Contoh tangkapan layar lebih banyak

Untuk menggunakan, letakkan teks Anda dalam wadah sebagai berikut:

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

Ketika fungsi jQuery berikut ditambahkan, salah satu div yang lebih besar dari nilai penyetelan akan dipotong dan memiliki tautan "Lainnya" ditambahkan.

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

Berdasarkan ini, tetapi diperbarui: http://shakenandstirredweb.com/240/jquery-moreless-text

Andy Beverley
sumber
<High> Saya pikir seseorang mungkin akan membatalkannya, mungkin karena itu bukan jawaban yang tepat untuk pertanyaan itu. Meskipun demikian, saya berharap seseorang akan merasakan manfaatnya, karena saya tidak bisa mendapatkan informasi ini di tempat lain dan ini adalah tempat saya mengakhiri setelah pencarian.
Andy Beverley
1

Plugin dotdotdot jQuery yang disebutkan berfungsi dengan baik dengan sudut:

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

Markup yang sesuai adalah:

<p app-ellipsis>{{ selectedItem.Description }}</p>
Edward Olamisan
sumber
1

Demo JS murni (tanpa jQuery dan loop 'sementara')

Ketika saya mencari solusi dari masalah ellipsis multiline, saya terkejut bahwa tidak ada yang bagus tanpa jQuery. Juga ada beberapa solusi berdasarkan loop 'while', tapi saya pikir mereka tidak efektif dan berbahaya karena kemungkinan untuk masuk ke loop infinite. Jadi saya menulis kode ini:

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);
WebBrother
sumber
1

Mungkin agak terlambat tetapi menggunakan SCSS Anda dapat mendeklarasikan fungsi seperti:

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

Dan gunakan seperti:

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

Yang akan memotong teks menjadi satu baris dan mengetahui bahwa itu adalah 1.4 tinggi barisnya. Output yang diharapkan adalah chrome untuk di-render dengan ...di akhir dan FF dengan beberapa fade keren di akhir

Firefox

masukkan deskripsi gambar di sini

Chrome

masukkan deskripsi gambar di sini

Alexis Duran
sumber
1

Temukan solusi singkat khusus CSS ini di jawaban Adrien Be :

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

Per Maret 2020, dukungan browser adalah 95,3% , tidak didukung di IE dan Opera Mini. Bekerja di Chrome, Safari, Firefox dan Edge.

Yulian
sumber
0

Anda mungkin tidak dapat melakukannya (saat ini?) Tanpa font dengan lebar tetap seperti Courier. Dengan font dengan lebar tetap, setiap huruf menempati ruang horizontal yang sama, sehingga Anda mungkin dapat menghitung huruf dan mengalikan hasilnya dengan ukuran font saat ini di ems atau exs. Maka Anda hanya perlu menguji berapa banyak huruf yang pas pada satu baris, dan kemudian memecahnya.

Atau, untuk font yang tidak diperbaiki Anda mungkin dapat membuat pemetaan untuk semua karakter yang mungkin (seperti i = 2px, m = 5px) dan kemudian melakukan perhitungan. Banyak pekerjaan buruk sekalipun.

DanMan
sumber
0

Untuk memperluas pada solusi @ DanMan: dalam kasus di mana font-lebar variabel digunakan, Anda bisa menggunakan lebar font rata-rata. Ini memiliki dua masalah: 1) teks dengan terlalu banyak W akan meluap dan 2) teks dengan terlalu banyak I akan terpotong lebih awal.

Atau Anda bisa mengambil pendekatan kasus terburuk dan menggunakan lebar huruf "W" (yang saya percaya adalah yang terluas). Ini menghilangkan masalah 1 di atas tetapi mengintensifkan masalah 2.

Pendekatan yang berbeda bisa berupa: tinggalkan overflow: clipdi div dan tambahkan bagian elipsis (mungkin div lain atau gambar) dengan float: right; position: relative; bottom: 0px;(belum diuji). Caranya adalah membuat gambar muncul di atas akhir teks.

Anda juga bisa hanya menampilkan gambar ketika Anda tahu itu akan meluap (katakanlah, setelah sekitar 100 karakter)

tidak pernah
sumber
Apa overflow: clip? Dan apa yang Anda harapkan dari CSS floatitu?
kapa
0

Dengan kode ini tidak perlu div pembungkus tambahan jika elemen memiliki tinggi dibatasi oleh gaya max-height.

// Shorten texts in overflowed paragraphs to emulate Operas text-overflow: -o-ellipsis-lastline
$('.ellipsis-lastline').each(function(i, e) {
    var $e = $(e), original_content = $e.text();
    while (e.scrollHeight > e.clientHeight)
        $e.text($e.text().replace(/\W*\w+\W*$/, '…'));
    $e.attr('data-original-content', original_content);
});

Juga menyimpan teks asli dalam atribut data yang dapat ditampilkan hanya menggunakan gaya, misalnya. di mouse:

.ellipsis-lastline {
    max-height: 5em;
}
.ellipsis-lastline:before {
    content: attr(data-original-content);
    position: absolute;
    display: none;
}
.ellipsis-lastline:hover:before {
    display: block;
}
Johan
sumber
1
Itu sering kali infinite loop.
Atadj
0

Dalam skenario saya, saya tidak dapat mengerjakan salah satu fungsi yang disebutkan di atas dan saya juga perlu memberi tahu fungsi berapa banyak baris yang harus ditampilkan terlepas dari ukuran font atau ukuran wadah.

Saya mendasarkan solusi saya pada penggunaan metode Canvas.measureText (yang merupakan fitur HTML5 ) seperti yang dijelaskan di sini oleh Domi , sehingga tidak sepenuhnya cross-browser.

Anda dapat melihat cara kerjanya pada biola ini .

Ini kodenya:

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

Dan HTML untuk menggunakannya adalah seperti ini:

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

Kode untuk mendapatkan font-family agak sederhana, dan dalam kasus saya berfungsi, tetapi untuk skenario yang lebih kompleks, Anda mungkin perlu menggunakan sesuatu di sepanjang baris ini .

Juga, dalam kasus saya, saya memberi tahu fungsi berapa banyak baris yang akan digunakan, tetapi Anda dapat menghitung berapa banyak baris untuk ditampilkan sesuai dengan ukuran dan font wadah.

Dzyann
sumber
0

Saya telah membuat versi yang membiarkan html tetap utuh. contoh 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;
}

contoh jsfiddle

Wow
sumber
0

Saya menulis komponen sudut yang memecahkan masalah. Ini membagi teks yang diberikan menjadi elemen span. Setelah rendering, ia menghilangkan semua elemen yang meluap dan menempatkan elips tepat setelah elemen yang terlihat terakhir.

Contoh penggunaan:

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

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

Komponen:

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];
  }
}
Martin Cremer
sumber
0

Di sini saya membuat perpustakaan lain dengan algoritma yang lebih cepat. Silakan periksa:

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

Untuk menginstal menggunakan bower:

bower install fast-ellipsis

Untuk menginstal menggunakan npm:

npm install fast-ellipsis 

Semoga kamu menikmati!

I. Ahmed
sumber
-2

tidak yakin apakah ini yang Anda cari, ia menggunakan ketinggian minimum, bukan ketinggian.

    <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>
khairil
sumber
4
Masalahnya adalah nomor 63 dalam kode Anda, jika nomor itu diketahui maka semuanya menjadi mudah, hanya fungsi terpotong melakukan pekerjaan ini, sebagai kode Anda. Namun, bagaimana cara mengetahui nomornya? Dengan kata lain, bagaimana cara mengetahui di mana teks akan dipecah baris? Jika pertanyaan ini dapat dijawab, maka masalahnya dapat diselesaikan dengan logika "1, hitung angkanya; 2, terpotong"
Edward
-3

Func sangat sederhana akan dilakukan.

Pengarahan:

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

Melihat:

<#p>{{truncateAlbumName(album.name)}}<#/p>
yamenator
sumber
3
Seperti yang sudah kita bahas di bawah jawaban lain, masalah dalam kode Anda adalah angka 36. Selain membuat kode Anda spesifik untuk lebar kontainer tertentu, itu juga tidak akurat: dengan font yang tidak memiliki lebar tetap, mungkin ada perbedaan besar antara huruf . Lihat iiiiiiiiiivs MMMMMMMMMM(dengan font saat ini tidak terlalu terlihat: D).
kapa