Détection d'ellipse de dépassement de texte HTML

176

J'ai une collection d'éléments de bloc sur une page. Ils ont tous les règles CSS espace blanc, débordement, débordement de texte définies afin que le texte débordant soit rogné et qu'une ellipse soit utilisée.

Cependant, tous les éléments ne débordent pas.

Puis-je utiliser javascript pour détecter les éléments qui débordent?

Merci.

Ajouté: exemple de structure HTML avec laquelle je travaille.

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

Les éléments SPAN tiennent toujours dans les cellules, ils ont la règle des points de suspension appliquée. Je veux détecter quand les points de suspension sont appliqués au contenu textuel du SPAN.

Deanoj
la source
10
Pas un double! Cette question porte sur un élément dans un autre élément parent. Je parle de texte dans un seul élément. Dans mon cas, le SPAN dans le TD ne déborde jamais du TD, c'est le texte dans le SPAN qui déborde et est coupé. C'est ce que j'essaye de détecter! Désolé - j'aurais pu mieux poser cette question, je l'avoue.
deanoj
Oh, j'ai oublié d'ajouter - cela n'a besoin de travailler sur webkit que si cela aide ...
deanoj
J'ai fait Ghommey juste pour voir si ça marchait ... ça n'a pas marché.
deanoj
l'aspect des points de suspension n'est pas pertinent; tout ce dont vous avez besoin pour détecter est s'il a débordé.
Spudley

Réponses:

114

Il était une fois, j'avais besoin de le faire, et la seule solution fiable pour tous les navigateurs que je rencontrais était le travail de piratage. Je ne suis pas le plus grand fan de solutions comme celle-ci, mais cela produit certainement le résultat correct à maintes reprises.

L'idée est que vous clonez l'élément, supprimez toute largeur de délimitation et testez si l'élément cloné est plus large que l'original. Si c'est le cas, vous savez que cela va avoir été tronqué.

Par exemple, en utilisant 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();

J'ai fait un jsFiddle pour le démontrer, http://jsfiddle.net/cgzW8/2/

Vous pouvez même créer votre propre pseudo-sélecteur personnalisé pour 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;
};

Ensuite, utilisez-le pour trouver des éléments

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

Démo: http://jsfiddle.net/cgzW8/293/

Espérons que cela aide, aussi piraté que cela puisse être.

Christian
la source
1
@Lauri Non; La troncature CSS ne modifie pas le texte réel dans la boîte, le contenu est donc toujours le même, qu'il soit tronqué, visible ou masqué. Il n'y a toujours aucun moyen d'obtenir le texte tronqué par programme, s'il y en avait, vous n'auriez pas besoin de cloner l'élément en premier lieu!
Christian
1
Il semble que cela ne fonctionnera pas dans des situations où il n'y en a pas white-space: nowrap.
Jakub Hampl
2
Je dois dire qu'après avoir recherché BEAUCOUP sur Internet et essayé de mettre en œuvre de nombreuses solutions, c'est de loin la plus fiable que j'ai trouvée. Cette solution ne donne pas de résultats différents entre les navigateurs comme element.innerWidth ou element.OffsetWidth qui ont des problèmes lors de l'utilisation des marges ou du padding .. Excellente solution, Bravo.
Scription le
1
Pour moi, cela n'a fonctionné nulle part. Je ne sais pas comment cela dépend du CSS (j'ai essayé de l'utiliser sur les contrôles d'entrée, les solutions ci-dessous fonctionnaient au moins dans Chrome et Firefox (mais pas dans IE11)) ...
Alexander
3
Excellente solution, en particulier en utilisant le pseudo-sélecteur jQuery. Mais parfois, cela peut ne pas fonctionner, car la largeur est calculée de manière incorrecte si le texte de l'élément a un style différent (taille de la police, espacement des lettres, marges des éléments internes). Dans ce cas, je recommanderais d'ajouter l'élément clone à $ this.parent () au lieu de 'body'. Cela donnera une copie exacte de l'élément et les calculs seront corrects. Merci quand même
Alex
287

Essayez cette fonction JS, en passant l'élément span comme argument:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}
Italo Borssatto
la source
10
Cela devrait être la réponse - très élégant et fonctionne sur Chrome et IE (9)
Roy Truelove
14
Cette réponse et celle d'Alex ne fonctionneront pas dans IE8; il y a des cas étranges où la largeur de défilement et la largeur extérieure sont les mêmes ... mais il y a des points de suspension, et puis certains cas où ils sont les mêmes ... et il n'y a PAS de points de suspension. En d'autres termes, la première solution est la seule qui fonctionne sur tous les navigateurs.
3
Pour ceux qui ont besoin de comprendre offsetWidth, scrollWidth et clientWidth, voici une très bonne explication: stackoverflow.com/a/21064102/1815779
Linh Dam
4
On text-overflow:ellipsisit should bee.offsetWidth <= e.scrollWidth
oriadam
4
Ils sont toujours égaux les uns aux autres sur les enfants flexibles et ne fonctionneront donc pas pour eux.
Kevin Beal
14

En plus de la réponse d'italo, vous pouvez également le faire en utilisant jQuery.

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

En outre, comme Smoky l'a souligné, vous souhaiterez peut-être utiliser jQuery externalWidth () au lieu de width ().

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

Pour ceux qui utilisent (ou prévoient d'utiliser) la réponse acceptée de Christian Varga, veuillez être conscient des problèmes de performances.

Le clonage / manipulation du DOM de cette manière provoque le refoulement du DOM (voir une explication sur ici) qui est extrêmement gourmand en ressources.

L'utilisation de la solution de Christian Varga sur plus de 100 éléments d'une page a provoqué un délai de redistribution de 4 secondes pendant lequel le thread JS est verrouillé. Considérant que JS est monothread, cela signifie un retard UX significatif pour l'utilisateur final.

La réponse d' Italo Borssatto devrait être celle acceptée, elle a été environ 10 fois plus rapide lors de mon profilage.

RyanGled
la source
2
Malheureusement, cela ne fonctionne pas dans tous les scénarios (j'aimerais bien). Et l'atteinte de performance que vous mentionnez ici peut être compensée en ne l'exécutant que pour la situation dans laquelle vous devez vérifier - comme uniquement sur mouseenterpour voir si une info-bulle doit être affichée.
Jacob
5

La réponse d' italo est très bonne! Cependant permettez-moi de l'affiner un peu:

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

Compatibilité entre navigateurs

Si, en fait, vous essayez le code ci-dessus et utilisez console.logpour imprimer les valeurs de e.offsetWidthet e.scrollWidth, vous remarquerez, sur IE, que, même lorsque vous n'avez pas de troncature de texte, une différence de valeur de 1pxou 2pxest constatée.

Alors, en fonction de la taille de police que vous utilisez, permettez une certaine tolérance!

Andry
la source
Cela ne fonctionne pas sur IE10 - offsetWidth est identique à scrollWidth, les deux me donnant la largeur tronquée.
SsjCosty
1
J'ai également besoin de cette tolérance sur Chrome 60 (le dernier).
Jan Żankowski
5

elem.offsetWdith VS ele.scrollWidth Ce travail pour moi! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});
Gustavo Juan
la source
Ni fonctionne dans Chrome actuel ni Firefox. Les deux éléments deviennent rouges.
xehpuk
4

Cet exemple affiche une info-bulle sur un tableau de cellules avec du texte tronqué. Est dynamique en fonction de la largeur de la table:

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

Démo: https://jsfiddle.net/t4qs3tqs/

Cela fonctionne sur toutes les versions de jQuery

rouge
la source
1

Je pense que le meilleur moyen de le détecter est de l'utiliser getClientRects(), il semble que chaque rect a la même hauteur, nous pouvons donc calculer le nombre de lignes avec le nombre de topvaleurs différentes .

getClientRectstravaille comme ça

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

getRowRectstravaille comme ça

tu peux détecter comme ça

Defims
la source
1

Toutes les solutions n'ont pas vraiment fonctionné pour moi, ce qui a fonctionné était de comparer les éléments scrollWidthauxscrollWidth de son parent (ou de son enfant, selon l'élément qui a le déclencheur).

Lorsque l'enfant scrollWidthest plus élevé que ses parents, cela signifie qu'il .text-ellipsisest actif.


Quand eventest l'élément parent

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

Quand eventest l'élément enfant

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

La e.offsetWidth < e.scrollWidthsolution ne fonctionne pas toujours.

Et si vous souhaitez utiliser du JavaScript pur, je vous recommande d'utiliser ceci:

(manuscrit)

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
la source
0

La solution @ItaloBorssatto est parfaite. Mais avant de regarder SO - j'ai pris ma décision. C'est ici :)

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>

Дмытрык
la source
0

Ma mise en œuvre)

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>

Дмытрык
la source