Erkennung von HTML-Textüberlauf-Auslassungspunkten

176

Ich habe eine Sammlung von Blockelementen auf einer Seite. Sie alle haben die CSS-Regeln Leerraum, Überlauf, Textüberlauf so eingestellt, dass überlaufender Text abgeschnitten und eine Ellipse verwendet wird.

Es laufen jedoch nicht alle Elemente über.

Kann ich mit Javascript erkennen, welche Elemente überlaufen?

Vielen Dank.

Hinzugefügt: Beispiel einer HTML-Struktur, mit der ich arbeite.

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

Die SPAN-Elemente passen immer in die Zellen, es wird die Ellipsenregel angewendet. Ich möchte erkennen, wann die Auslassungspunkte auf den Textinhalt des SPAN angewendet werden.

Deanoj
quelle
10
Kein Duplikat! Diese Frage bezieht sich auf ein Element in einem anderen übergeordneten Element. Ich spreche von Text innerhalb eines einzelnen Elements. In meinem Fall läuft der SPAN im TD nie über den TD hinaus. Es ist der Text im SPAN, der überläuft und gekürzt wird. Das versuche ich zu erkennen! Entschuldigung - ich hätte diese Frage besser stellen können, gebe ich zu.
Deanoj
Oh, ich habe vergessen hinzuzufügen - dies muss nur am Webkit funktionieren, wenn das hilft ...
Deanoj
Ich habe Ghommey gemacht, nur um zu sehen, ob es funktioniert hat ... es hat nicht funktioniert.
Deanoj
der Auslassungsaspekt ist irrelevant; Sie müssen lediglich feststellen, ob es übergelaufen ist.
Spudley

Antworten:

114

Es war einmal, dass ich dies tun musste, und die einzige browserübergreifende zuverlässige Lösung, auf die ich stieß, war Hackjob. Ich bin nicht der größte Fan solcher Lösungen, aber es liefert immer wieder das richtige Ergebnis.

Die Idee ist, dass Sie das Element klonen, die Begrenzungsbreite entfernen und testen, ob das geklonte Element breiter als das Original ist. Wenn ja, wissen Sie, dass es abgeschnitten worden sein wird.

Beispiel: Verwenden von 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();

Ich habe eine jsFiddle erstellt, um dies zu demonstrieren: http://jsfiddle.net/cgzW8/2/

Sie können sogar Ihren eigenen benutzerdefinierten Pseudo-Selektor für jQuery erstellen:

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

Verwenden Sie es dann, um Elemente zu finden

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

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

Hoffentlich hilft das, hacky wie es ist.

Christian
quelle
1
@Lauri Nein; Durch das Abschneiden von CSS wird der tatsächliche Text im Feld nicht geändert , sodass der Inhalt immer derselbe ist, unabhängig davon, ob er abgeschnitten, sichtbar oder ausgeblendet ist. Es gibt immer noch keine Möglichkeit, den abgeschnittenen Text programmgesteuert abzurufen. Wenn dies der Fall wäre, müssten Sie das Element gar nicht erst klonen!
Christian
1
Scheint, dass dies in Situationen, in denen es keine gibt, nicht funktioniert white-space: nowrap.
Jakub Hampl
2
Ich muss sagen, dass dies nach der Suche im Internet und dem Versuch, viele Lösungen zu implementieren, die mit Abstand zuverlässigste ist, die ich gefunden habe. Diese Lösung liefert keine unterschiedlichen Ergebnisse zwischen Browsern wie element.innerWidth oder element.OffsetWidth, bei denen Probleme bei der Verwendung von Rändern oder Auffüllungen auftreten. Tolle Lösung, gut gemacht.
Scription
1
Für mich hat das nirgendwo funktioniert. Ich bin nicht sicher, wie es von CSS abhängt (ich habe versucht, es für Eingabesteuerungen zu verwenden, die folgenden Lösungen funktionierten zumindest in Chrome und Firefox (aber nicht in IE11)) ...
Alexander
3
Tolle Lösung, insbesondere mit dem jQuery-Pseudo-Selektor. Manchmal funktioniert dies jedoch möglicherweise nicht, da die Breite falsch berechnet wird, wenn der Elementtext einen anderen Stil hat (Schriftgröße, Buchstabenabstand, Ränder der inneren Elemente). In diesem Fall würde ich empfehlen, das Klonelement an $ this.parent () anstelle von 'body' anzuhängen. Dies gibt eine genaue Kopie des Elements und die Berechnungen sind korrekt.
Alex
286

Probieren Sie diese JS-Funktion aus und übergeben Sie das span-Element als Argument:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}
Italo Borssatto
quelle
10
Dies sollte die Antwort sein - sehr elegant und funktioniert auf Chrome und IE (9)
Roy Truelove
14
Diese Antwort und Alex 'Antwort funktionieren in IE8 nicht. Es gibt einige seltsame Fälle, in denen die Bildlaufbreite und die Außenbreite gleich sind ... aber es gibt Auslassungspunkte, und dann einige Fälle, in denen sie gleich sind ... und es gibt KEINE Auslassungspunkte. Mit anderen Worten, die erste Lösung ist die einzige, die in allen Browsern funktioniert.
3
Für diejenigen, die OffsetWidth, ScrollWidth und ClientWidth verstehen müssen, ist hier eine sehr gute Erklärung: stackoverflow.com/a/21064102/1815779
Linh Dam
4
Darauf text-overflow:ellipsissollte seine.offsetWidth <= e.scrollWidth
Oriadam
4
Sie sind bei flexiblen Kindern immer gleich und funktionieren daher nicht für sie.
Kevin Beal
14

Zusätzlich zur Antwort von italo können Sie dies auch mit jQuery tun.

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

Wie Smoky hervorhob, möchten Sie möglicherweise auch jQuery OuterWidth () anstelle von width () verwenden.

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

Wenn Sie die akzeptierte Antwort von Christian Varga verwenden (oder planen, diese zu verwenden), beachten Sie bitte die Leistungsprobleme.

Das Klonen / Bearbeiten des DOM auf diese Weise führt zu einem DOM-Reflow (siehe Erläuterung zum DOM-Reflow hier), der äußerst ressourcenintensiv ist.

Die Verwendung der Lösung von Christian Varga für mehr als 100 Elemente auf einer Seite verursachte eine Reflow-Verzögerung von 4 Sekunden, während der der JS-Thread gesperrt ist. Wenn man bedenkt, dass JS Single-Threaded ist, bedeutet dies eine erhebliche UX-Verzögerung für den Endbenutzer.

Die Antwort von Italo Borssatto sollte die akzeptierte sein, sie war während meiner Profilerstellung ungefähr zehnmal schneller.

RyanGled
quelle
2
Leider funktioniert es nicht in jedem Szenario (ich wünschte, es würde). Und der hier erwähnte Leistungseinbruch kann ausgeglichen werden, indem er nur für Situationen ausgeführt wird, in denen Sie überprüfen müssen - wie nur mouseenter, um festzustellen, ob ein Tooltip angezeigt werden muss.
Jacob
5

Antwort von italo ist sehr gut! Lassen Sie es mich jedoch ein wenig verfeinern:

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

Cross-Browser-Kompatibilität

Wenn Sie tatsächlich den obigen Code ausprobieren und console.logzum Ausdrucken der Werte von e.offsetWidthund verwenden e.scrollWidth, werden Sie im IE feststellen, dass selbst wenn Sie keine Textabschneidung haben, eine Wertdifferenz von 1pxoder2px auftritt.

Lassen Sie also je nach verwendeter Schriftgröße eine bestimmte Toleranz zu!

Andry
quelle
Unter IE10 funktioniert es nicht - offsetWidth ist identisch mit scrollWidth. Beide geben mir die abgeschnittene Breite.
SsjCosty
1
Ich brauche diese Toleranz auch für Chrome 60 (aktuell).
Jan Żankowski
5

elem.offsetWdith VS ele.scrollWidth Diese Arbeit für mich! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});
Gustavo Juan
quelle
Funktioniert weder in Chrome noch in Firefox. Beide Elemente werden rot.
Xehpuk
4

In diesem Beispiel wird ein Tooltip für eine Zelltabelle mit abgeschnittenem Text angezeigt. Ist dynamisch basierend auf der Tabellenbreite:

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

Es funktioniert auf allen Versionen von jQuery

rot
quelle
1

Ich denke, der bessere Weg, dies zu erkennen, ist die Verwendung getClientRects(). Es scheint, dass jedes Rechteck die gleiche Höhe hat, sodass wir die Zeilennummer mit der Anzahl der unterschiedlichen topWerte berechnen können.

getClientRectsarbeite so

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

getRowRectsarbeite so

Sie kann wie erkennen diese

Defims
quelle
1

Alle Lösungen haben bei mir nicht wirklich funktioniert. Es hat funktioniert, die Elemente scrollWidthmit denen scrollWidthdes Elternteils (oder des Kindes, je nachdem, welches Element den Auslöser hat) zu vergleichen.

Wenn das Kind scrollWidthhöher ist als seine Eltern, bedeutet dies, dass .text-ellipsises aktiv ist.


Wann eventist das übergeordnete Element?

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

Wann eventist das untergeordnete Element?

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

Die e.offsetWidth < e.scrollWidthLösung funktioniert nicht immer.

Und wenn Sie reines JavaScript verwenden möchten, empfehle ich Folgendes:

(Typoskript)

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

Die Lösung @ItaloBorssatto ist perfekt. Aber bevor ich mir SO ansah, traf ich meine Entscheidung. Hier ist es :)

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>

Дмытрык
quelle
0

Meine Implementierung)

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>

Дмытрык
quelle