Deteksi elipsis teks-overflow HTML

176

Saya memiliki koleksi elemen blok pada halaman. Mereka semua memiliki aturan CSS white-space, overflow, set teks-overflow sehingga teks yang meluap dipangkas dan elipsis digunakan.

Namun, tidak semua elemen meluap.

Adakah saya bisa menggunakan javascript untuk mendeteksi elemen mana yang meluap?

Terima kasih.

Ditambahkan: contoh struktur HTML yang saya kerjakan.

<td><span>Normal text</span></td>
<td><span>Long text that will be trimmed text</span></td>

Elemen SPAN selalu muat di dalam sel, mereka memiliki aturan elips yang diterapkan. Saya ingin mendeteksi kapan elipsis diterapkan ke konten teks SPAN.

deanoj
sumber
10
Bukan duplikat! Pertanyaan itu adalah tentang satu elemen di dalam elemen lain, elemen induk. Saya berbicara tentang teks dalam satu elemen. Dalam kasus saya, SPAN di TD tidak pernah meluap TD, itu teks dalam SPAN yang meluap, dan akan dipangkas. Itulah yang saya coba deteksi! Maaf - saya bisa mengajukan pertanyaan ini lebih baik saya akui.
deanoj
Oh, saya lupa menambahkan - ini hanya perlu bekerja di webkit jika itu membantu ...
deanoj
Saya melakukan Ghommey hanya untuk melihat apakah itu berhasil ... tidak.
deanoj
aspek ellipsis tidak relevan; yang perlu Anda deteksi adalah apakah meluap.
Spudley

Jawaban:

114

Sekali waktu saya perlu melakukan ini, dan satu-satunya solusi yang dapat diandalkan lintas-browser saya temui adalah pekerjaan hack. Saya bukan penggemar solusi terbesar seperti ini, tetapi tentu saja menghasilkan hasil yang benar berulang kali.

Idenya adalah Anda mengkloning elemen, menghapus lebar ikatan apa pun, dan menguji apakah elemen yang dikloning lebih lebar dari aslinya. Jika demikian, Anda tahu itu akan terpotong.

Misalnya, menggunakan jQuery:

var $element = $('#element-to-test');
var $c = $element
           .clone()
           .css({display: 'inline', width: 'auto', visibility: 'hidden'})
           .appendTo('body');

if( $c.width() > $element.width() ) {
    // text was truncated. 
    // do what you need to do
}

$c.remove();

Saya membuat jsFiddle untuk mendemonstrasikan ini, http://jsfiddle.net/cgzW8/2/

Anda bahkan dapat membuat pemilih-semu kustom Anda sendiri untuk jQuery:

$.expr[':'].truncated = function(obj) {
  var $this = $(obj);
  var $c = $this
             .clone()
             .css({display: 'inline', width: 'auto', visibility: 'hidden'})
             .appendTo('body');

  var c_width = $c.width();
  $c.remove();

  if ( c_width > $this.width() )
    return true;
  else
    return false;
};

Kemudian gunakan untuk menemukan elemen

$truncated_elements = $('.my-selector:truncated');

Demo: http://jsfiddle.net/cgzW8/293/

Semoga ini bisa membantu, hacky apa adanya.

Kristen
sumber
1
@Lauri Tidak; Pemotongan CSS tidak mengubah teks aktual di dalam kotak, jadi kontennya selalu sama, apakah terpotong, terlihat, atau disembunyikan. Masih tidak ada cara untuk secara terprogram mendapatkan teks terpotong, jika ada maka Anda tidak perlu mengkloning elemen di tempat pertama!
Christian
1
Tampaknya ini tidak akan berfungsi dalam situasi di mana tidak ada white-space: nowrap.
Jakub Hampl
2
Saya harus mengatakan bahwa setelah mencari BANYAK di internet dan mencoba menerapkan banyak solusi, sejauh ini yang paling dapat diandalkan yang saya temukan. Solusi ini tidak memberikan hasil yang berbeda antara browser seperti element.innerWidth atau element.OffsetWidth apakah memiliki masalah saat menggunakan margin atau padding .. Solusi hebat, Bagus sekali.
Scription
1
Bagi saya, ini tidak berhasil di mana pun. Saya tidak yakin, bagaimana ini tergantung pada CSS (saya sudah mencoba menggunakannya pada kontrol input, solusi di bawah ini bekerja setidaknya di Chrome dan Firefox (tetapi tidak di IE11)) ...
Alexander
3
Solusi hebat, terutama menggunakan jQuery pseudo-selector. Tetapi kadang-kadang mungkin tidak berfungsi, karena lebar dihitung secara tidak benar jika teks elemen memiliki gaya yang berbeda (ukuran font, spasi huruf, margin elemen dalam). Dalam hal ini saya akan merekomendasikan untuk menambahkan elemen clone ke $ this.parent () alih-alih 'tubuh'. Ini akan memberikan salinan elemen yang tepat dan perhitungan akan benar. Terima kasih
Alex
286

Coba fungsi JS ini, dengan meneruskan elemen span sebagai argumen:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}
Italo Borssatto
sumber
10
Ini seharusnya jawabannya - sangat elegan dan berfungsi di Chrome dan IE (9)
Roy Truelove
14
Jawaban ini dan jawaban Alex tidak akan berfungsi di IE8; ada beberapa kasus aneh di mana lebar dan lebar gulir sama dengan ... tetapi memiliki ellipsis, dan kemudian beberapa kasus di mana mereka sama ... dan TIDAK ada ellipsis. Dengan kata lain, solusi pertama adalah satu-satunya yang berfungsi di semua browser.
3
Bagi mereka yang perlu memahami offsetWidth, scrollWidth, dan clientWidth, berikut adalah penjelasan yang sangat bagus: stackoverflow.com/a/21064102/1815779
Linh Dam
4
Di text-overflow:ellipsisatasnya haruse.offsetWidth <= e.scrollWidth
oriadam
4
Mereka selalu sama satu sama lain pada anak fleksibel, dan dengan demikian, tidak akan bekerja untuk mereka.
Kevin Beal
14

Menambahkan ke jawaban italo, Anda juga dapat melakukan ini menggunakan jQuery.

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.width() < $jQueryObject[0].scrollWidth);
}

Juga, seperti yang ditunjukkan Smoky, Anda mungkin ingin menggunakan jQuery outerWidth () alih-alih lebar ().

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.outerWidth() < $jQueryObject[0].scrollWidth);
}
Alex K
sumber
9

Bagi mereka yang menggunakan (atau berencana untuk menggunakan) jawaban yang diterima dari Christian Varga, harap ketahui masalah kinerja.

Kloning / memanipulasi DOM sedemikian rupa menyebabkan DOM Reflow (lihat penjelasan tentang DOM reflow di sini) yang sangat intensif sumber daya.

Menggunakan solusi Christian Varga pada 100+ elemen pada halaman menyebabkan penundaan reflow selama 4 detik di mana utas JS dikunci. Mengingat JS adalah single-threaded, ini berarti penundaan UX yang signifikan bagi pengguna akhir.

Italo Borssatto ini jawabannya harus menjadi salah satu yang diterima, itu kira-kira 10 kali lebih cepat selama profiling saya.

RyanGled
sumber
2
Sayangnya tidak berfungsi di setiap skenario (saya berharap demikian). Dan performa yang Anda sebutkan di sini dapat diimbangi dengan hanya menjalankannya untuk situasi di mana Anda perlu memeriksa - seperti hanya mouseenteruntuk melihat apakah tooltip perlu ditampilkan.
Yakub
5

Jawaban dari italo sangat bagus! Namun izinkan saya sedikit memperbaikinya:

function isEllipsisActive(e) {
   var tolerance = 2; // In px. Depends on the font you are using
   return e.offsetWidth + tolerance < e.scrollWidth;
}

Kompatibilitas lintas browser

Jika, pada kenyataannya, Anda mencoba kode di atas dan menggunakannya console.loguntuk mencetak nilai-nilai e.offsetWidthdan e.scrollWidth, Anda akan melihat, di IE, bahwa, bahkan ketika Anda tidak memiliki pemotongan teks, perbedaan nilai 1pxatau2px dialami.

Jadi, tergantung pada ukuran font yang Anda gunakan, izinkan toleransi tertentu!

Andry
sumber
Itu tidak bekerja pada IE10 - offsetWidth identik dengan scrollWidth, keduanya memberi saya lebar terpotong.
SsjCosty
1
Saya perlu toleransi ini di Chrome 60 (terbaru) juga.
Jan Żankowski
5

elem.offsetWdith VS ele.scrollWidth Ini bekerja untuk saya! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});
Gustavo Juan
sumber
Tidak berfungsi di Chrome saat ini atau Firefox. Kedua elemen menjadi merah.
xehpuk
4

Sampel ini menunjukkan tooltip pada tabel sel dengan teks terpotong. Dinamis berdasarkan lebar tabel:

$.expr[':'].truncated = function (obj) {
    var element = $(obj);

    return (element[0].scrollHeight > (element.innerHeight() + 1)) || (element[0].scrollWidth > (element.innerWidth() + 1));
};

$(document).ready(function () {
    $("td").mouseenter(function () {
        var cella = $(this);
        var isTruncated = cella.filter(":truncated").length > 0;
        if (isTruncated) 
            cella.attr("title", cella.text());
        else 
            cella.attr("title", null);
    });
});

Demo: https://jsfiddle.net/t4qs3tqs/

Ini bekerja pada semua versi jQuery

Merah
sumber
1

Saya pikir cara yang lebih baik untuk mendeteksinya adalah menggunakan getClientRects(), tampaknya setiap kotak memiliki tinggi yang sama, sehingga kita dapat menghitung angka garis dengan jumlah topnilai yang berbeda .

getClientRectsbekerja seperti ini

function getRowRects(element) {
    var rects = [],
        clientRects = element.getClientRects(),
        len = clientRects.length,
        clientRect, top, rectsLen, rect, i;

    for(i=0; i<len; i++) {
        has = false;
        rectsLen = rects.length;
        clientRect = clientRects[i];
        top = clientRect.top;
        while(rectsLen--) {
            rect = rects[rectsLen];
            if (rect.top == top) {
                has = true;
                break;
            }
        }
        if(has) {
            rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
            rect.width = rect.right - rect.left;
        }
        else {
            rects.push({
                top: clientRect.top,
                right: clientRect.right,
                bottom: clientRect.bottom,
                left: clientRect.left,
                width: clientRect.width,
                height: clientRect.height
            });
        }
    }
    return rects;
}

getRowRectsbekerja seperti ini

Anda dapat mendeteksi seperti ini

Pakaian
sumber
1

Semua solusi yang tidak benar-benar bekerja untuk saya, apa yang lakukan pekerjaan yang membandingkan elemen scrollWidthkescrollWidth induknya (atau anak, tergantung pada elemen memiliki pemicu).

Ketika anak scrollWidthlebih tinggi dari orang tuanya, itu berarti .text-ellipsisaktif.


Kapan eventelemen induk

function isEllipsisActive(event) {
    let el          = event.currentTarget;
    let width       = el.offsetWidth;
    let widthChild  = el.firstChild.offsetWidth;
    return (widthChild >= width);
}

Kapan eventelemen anak

function isEllipsisActive(event) {
    let el          = event.currentTarget;
    let width       = el.offsetWidth;
    let widthParent = el.parentElement.scrollWidth;
    return (width >= widthParent);
}
Jeffrey Roosendaal
sumber
0

Itu e.offsetWidth < e.scrollWidth solusi tidak selalu bekerja.

Dan jika Anda ingin menggunakan JavaScript murni, saya sarankan untuk menggunakan ini:

(naskah)

public isEllipsisActive(element: HTMLElement): boolean {
    element.style.overflow = 'initial';
    const noEllipsisWidth = element.offsetWidth;
    element.style.overflow = 'hidden';
    const ellipsisWidth = element.offsetWidth;

    if (ellipsisWidth < noEllipsisWidth) {
      return true;
    } else {
      return false;
    }
}
Sarel Zioni
sumber
0

Solusi @ItaloBorssatto sempurna. Tetapi sebelum melihat SO - saya membuat keputusan. Ini dia :)

const elems = document.querySelectorAll('span');
elems.forEach(elem => {
  checkEllipsis(elem);
});

function checkEllipsis(elem){
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const styles = getComputedStyle(elem);
  ctx.font = `${styles.fontWeight} ${styles.fontSize} ${styles.fontFamily}`;
  const widthTxt = ctx.measureText(elem.innerText).width;
  if (widthTxt > parseFloat(styles.width)){
    elem.style.color = 'red'
  }
}
span.cat {
    display: block;
    border: 1px solid black;
    white-space: nowrap;
    width: 100px;
    overflow: hidden;
    text-overflow: ellipsis;
}
 <span class="cat">Small Cat</span>
      <span class="cat">Looooooooooooooong Cat</span>

Дмытрык
sumber
0

Implementasi saya)

const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
    item.style.color = checkEllipsis(item) ? 'red': 'black'
})

function checkEllipsis(el){
  const styles = getComputedStyle(el);
  const widthEl = parseFloat(styles.width);
  const ctx = document.createElement('canvas').getContext('2d');
  ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
  const text = ctx.measureText(el.innerText);
  return text.width > widthEl;
}
.item{
  width: 60px;
  overflow: hidden;
  text-overflow: ellipsis;
}
      <div class="item">Short</div>
      <div class="item">Loooooooooooong</div>

Дмытрык
sumber