Rilevamento ellissi HTML con overflow del testo


176

Ho una raccolta di elementi a blocchi su una pagina. Tutti hanno le regole CSS impostate su spazio bianco, overflow, text-overflow in modo che il testo traboccante venga tagliato e vengano utilizzati i puntini di sospensione.

Tuttavia, non tutti gli elementi traboccano.

Posso comunque usare javascript per rilevare quali elementi traboccano?

Grazie.

Aggiunto: esempio di struttura HTML con cui sto lavorando.

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

Gli elementi SPAN si adattano sempre alle celle, hanno applicato la regola dei puntini di sospensione. Voglio rilevare quando i puntini di sospensione vengono applicati al contenuto testuale di SPAN.



10
Non un duplicato! Questa domanda riguarda un elemento all'interno di un altro elemento genitore. Sto parlando di testo all'interno di un singolo elemento. Nel mio caso, lo SPAN nel TD non trabocca mai nel TD, è il testo all'interno dello SPAN che trabocca e viene tagliato. Questo è quello che sto cercando di rilevare! Scusa, avrei potuto porre questa domanda meglio, lo ammetto.
deanoj,

Oh, ho dimenticato di aggiungere - questo ha bisogno di funzionare solo su webkit se questo aiuta ...
deanoj,

Ho fatto Ghommey solo per vedere se funzionava ... non ha funzionato.
deanoj,

l'aspetto dei puntini di sospensione è irrilevante; tutto ciò che devi rilevare è se è traboccato.
Spudley,

Risposte:


114

Una volta avevo bisogno di fare questo, e l'unica soluzione affidabile tra browser che mi sono imbattuto è stato il lavoro di hacking. Non sono il più grande fan di soluzioni come questa, ma certamente produce sempre il risultato corretto.

L'idea è di clonare l'elemento, rimuovere qualsiasi larghezza di delimitazione e verificare se l'elemento clonato è più largo dell'originale. Se è così, sai che sarà troncato.

Ad esempio, usando 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();

Ho creato un jsFiddle per dimostrarlo, http://jsfiddle.net/cgzW8/2/

Puoi persino creare il tuo pseudo-selettore personalizzato per 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;
};

Quindi usalo per trovare elementi

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

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

Speriamo che questo aiuti, per quanto confuso.


1
@Lauri No; Il troncamento CSS non cambia il testo effettivo nella casella, quindi il contenuto è sempre lo stesso, sia troncato, visibile o nascosto. Non c'è ancora modo di ottenere a livello di codice il testo troncato, se ci fosse allora non sarebbe necessario clonare l'elemento in primo luogo!
Christian,

1
Sembra che questo non funzionerà in situazioni in cui non c'è white-space: nowrap.
Jakub Hampl,

2
Devo dire che dopo aver cercato MOLTO su Internet e provato a implementare molte soluzioni, questa è di gran lunga la più affidabile che ho trovato. Questa soluzione non fornisce risultati diversi tra browser come element.innerWidth o element.OffsetWidth che ha problemi nell'utilizzo dei margini o del riempimento. Ottima soluzione, ben fatto.
Descrizione

1
Per me, questo non ha funzionato da nessuna parte. Non sono sicuro di come dipenda dal CSS (ho provato ad usarlo sui controlli di input, le soluzioni di seguito hanno funzionato almeno in Chrome e Firefox (ma non in IE11)) ...
Alexander

3
Ottima soluzione, in particolare utilizzando lo pseudo-selettore jQuery. Ma a volte potrebbe non funzionare, perché la larghezza viene calcolata in modo errato se il testo dell'elemento ha uno stile diverso (dimensione del carattere, spaziatura tra le lettere, margini degli elementi interni). In tal caso, consiglierei di aggiungere l'elemento clone a $ this.parent () invece di 'body'. Ciò fornirà una copia esatta dell'elemento e i calcoli saranno corretti. Grazie comunque
Alex,

287

Prova questa funzione JS, passando l'elemento span come argomento:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}

10
Questa dovrebbe essere la risposta - molto elegante e funziona su Chrome e IE (9)
Roy Truelove,

14
Questa risposta e la risposta di Alex non funzioneranno in IE8; ci sono alcuni casi strani in cui la larghezza del rotolo e la larghezza esterna sono uguali ... ma ha i puntini di sospensione, e poi alcuni casi in cui sono uguali ... e NON ci sono puntini di sospensione. In altre parole, la prima soluzione è l'unica che funziona su tutti i browser.

3
Per coloro che hanno bisogno di capire offsetWidth, scrollWidth e clientWidth, ecco una spiegazione molto buona: stackoverflow.com/a/21064102/1815779
Linh Dam

4
Su text-overflow:ellipsisdovrebbe esseree.offsetWidth <= e.scrollWidth
Oriadam

4
Sono sempre uguali tra loro sui bambini flessibili e quindi non funzioneranno per loro.
Kevin Beal,

14

Aggiungendo alla risposta di italo, puoi anche farlo usando jQuery.

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

Inoltre, come ha sottolineato Smoky, potresti voler usare jQuery outerWidth () invece di width ().

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

9

Per coloro che utilizzano (o hanno intenzione di utilizzare) la risposta accettata da Christian Varga, si prega di essere consapevoli dei problemi di performance.

La clonazione / manipolazione del DOM in questo modo provoca il riflusso del DOM (vedere una spiegazione sul riflusso del DOM qui) che richiede molta risorse.

L'uso della soluzione di Christian Varga su oltre 100 elementi in una pagina ha causato un ritardo di riflusso di 4 secondi durante il quale il thread JS è bloccato. Considerando che JS è a thread singolo, ciò significa un ritardo UX significativo per l'utente finale.

La risposta di Italo Borssatto dovrebbe essere quella accettata, è stata circa 10 volte più veloce durante la mia profilazione.


2
Purtroppo non funziona in tutti gli scenari (vorrei che lo facesse). E il colpo di performance che menzionate qui può essere compensato eseguendolo solo per situazioni in cui è necessario verificare, ad esempio solo mouseenterper vedere se è necessario visualizzare una descrizione comandi.
Jacob,

5

La risposta di italo è molto buona! Tuttavia, consentitemi di perfezionarlo un po ':

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

Compatibilità tra browser

Se, in effetti, provi il codice sopra e utilizzi console.logper stampare i valori di e.offsetWidthe e.scrollWidth, noterai, su IE, che, anche quando non hai il troncamento del testo, si verifica una differenza di valore 1pxo 2px.

Quindi, a seconda della dimensione del carattere che usi, consenti una certa tolleranza!


Non funziona su IE10 - offsetWidth è identico a scrollWidth, entrambi mi danno la larghezza troncata.
SsjCosty,

1
Ho bisogno di questa tolleranza anche su Chrome 60 (più recente).
Jan Żankowski,

5

elem.offsetWdith VS ele.scrollWidth Questo lavoro per me! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});

Né funziona nell'attuale Chrome né in Firefox. Entrambi gli elementi diventano rossi.
xehpuk,

4

Questo esempio mostra la descrizione comandi sulla tabella di celle con testo troncato. È dinamico in base alla larghezza della tabella:

$.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/

Funziona su tutte le versioni di jQuery


1

Penso che il modo migliore per rilevarlo sia l'uso getClientRects(), sembra che ogni rettangolo abbia la stessa altezza, quindi possiamo calcolare il numero di linee con il numero di diverso topvalore.

getClientRectsun'opera come questa

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

getRowRectsun'opera come questa

puoi rilevare in questo modo


1

Tutte le soluzioni non hanno funzionato davvero per me, quello che ha funzionato è stato confrontare gli elementi scrollWidthcon quelli scrollWidthdel suo genitore (o figlio, a seconda dell'elemento che ha il trigger).

Quando il bambino scrollWidthè più alto dei suoi genitori, significa che .text-ellipsisè attivo.


Quando eventè l'elemento genitore

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

Quando eventè l'elemento figlio

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

0

La e.offsetWidth < e.scrollWidthsoluzione non funziona sempre.

E se vuoi usare JavaScript puro, ti consiglio di usare questo:

(dattiloscritto)

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

0

La soluzione @ItaloBorssatto è perfetta. Ma prima di guardare SO - ho preso la mia decisione. Ecco qui :)

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>


0

La mia implementazione)

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>


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.