HTMLテキストのオーバーフロー省略検出


176

ページにブロック要素のコレクションがあります。CSSルールの空白、オーバーフロー、テキストオーバーフローがすべて設定されているため、オーバーフローしたテキストは削除され、省略記号が使用されます。

ただし、すべての要素がオーバーフローするわけではありません。

とにかく、どの要素がオーバーフローしているかを検出するためにJavaScriptを使用できますか?

ありがとう。

追加:使用しているHTML構造の例。

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

SPAN要素は常にセルに収まり、省略記号ルールが適用されます。省略記号がSPANのテキストコンテンツにいつ適用されるかを検出したいと思います。



10
重複ではありません!その質問は、別の親要素内のある要素についてです。単一の要素内のテキストについて話している。私の場合、TD内のSPANがTDをオーバーフローすることはありません。オーバーフローし、トリミングされるのは、SPAN内のテキストです。それは私が検出しようとしているものです!申し訳ありませんが、私はこの質問を私が認めたほうがいいでしょう。
deanoj 2011年

ああ、私は追加するのを忘れました-これが役立つ場合にのみwebkitで作業する必要があります...
deanoj

Ghommeyが機能するかどうかを確認するためだけに実行しました。機能しませんでした。
deanoj 2011年

省略の側面は無関係です。検出する必要があるのは、それがオーバーフローしているかどうかだけです。
Spudley、2011年

回答:


114

むかしむかし、これを行う必要があり、ブラウザ間で信頼できる唯一の解決策はハックジョブでした。私はこのようなソリューションの最大のファンではありませんが、確かに何度も正しい結果を生み出します。

アイデアは、要素を複製し、境界幅を削除し、複製した要素が元の要素よりも広いかどうかをテストすることです。もしそうなら、あなたはそれが切り捨てられるだろうことを知っています。

たとえば、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();

これを実証するためにjsFiddleを作成しました、http: //jsfiddle.net/cgzW8/2/

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

次に、それを使用して要素を検索します

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

デモ:http : //jsfiddle.net/cgzW8/293/

うまくいけば、これは、ハックがそのままであることを助けます。


1
@Lauriいいえ。CSSの切り捨てによってボックス内の実際のテキストが変更されることはないため、切り捨て、表示、非表示のいずれの場合でも、コンテンツは常に同じです。プログラムで切り詰められたテキストを取得する方法はまだありません。その場合、最初に要素を複製する必要はありません!
クリスチャン

1
がない場合、これは機能しないようwhite-space: nowrapです。
Jakub Hampl 2014

2
インターネットでLOTを検索し、多くのソリューションを実装しようとした後、これは私が見つけた中でこれまでで最も信頼できるものだと言わなければなりません。このソリューションはelement.innerWidthやelement.OffsetWidthのようなブラウザ間で異なる結果を与えません。マージンやパディングを使用するときに問題があります。素晴らしいソリューションです。よくできました。
スクリプト

1
私にとって、これはどこでも機能しませんでした。CSSにどのように依存するのかわかりません(入力コントロールで使用しようとしましたが、以下の解決策は少なくともChromeとFirefox(IE11では機能しません)で機能しました)...
Alexander

3
特にjQuery疑似セレクターを使用する優れたソリューション。ただし、要素テキストのスタイル(フォントサイズ、文字間隔、内部要素の余白)が異なる場合、幅が正しく計算されないため、機能しない場合があります。その場合、「this」の代わりにclone要素を「this」に追加することをお勧めします。これにより、要素の正確なコピーが得られ、計算が正しく行われます。とにかくありがとう
アレックス

286

このJS関数を試して、span要素を引数として渡します。

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

10
これが答えになるはずです。非常にエレガントで、ChromeとIE(9)で動作します
Roy Truelove 2013年

14
この回答とAlexの回答はIE8では機能しません。スクロール幅と外幅が同じであるいくつかの奇妙なケースがあります...しかし、省略記号があり、それらが同じであるいくつかのケース...そして省略記号がありません。言い換えると、最初のソリューションは、すべてのブラウザーで機能する唯一のソリューションです。

3
offsetWidth、scrollWidth、およびclientWidthを理解する必要がある人のために、ここに非常に良い説明があります:stackoverflow.com/a/21064102/1815779
Linh Dam

4
text-overflow:ellipsis、それがあるべきe.offsetWidth <= e.scrollWidth
oriadam

4
それらはflexの子では常に互いに等しいため、それらのために機能しません。
Kevin Beal

14

italoの答えに加えて、jQueryを使用してこれを行うこともできます。

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

また、Smokyが指摘したように、width()ではなくjQuery outerWidth()を使用することもできます。

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

9

Christian Vargaの承認済みの回答を使用している(または使用を計画している)場合は、パフォーマンスの問題に注意してください。

このような方法でDOMを複製 /操作すると、非常に多くのリソースを消費するDOMリフロー(ここでDOMリフローの説明を参照)が発生します

ページ上の100以上の要素に対してChristian Vargaのソリューションを使用すると、JSスレッドがロックされている間、4秒のリフロー遅延が発生しました。JSがシングルスレッドであることを考えると、これはエンドユーザーにとってUXの大幅な遅延を意味します。

Italo Borssattoの回答は受け入れられるはずです。私のプロファイリングでは約10倍速くなりました。


2
残念ながら、それはすべてのシナリオで機能するわけではありません(うまくいくといいのですが)。ここで言及するパフォーマンスヒットはmouseenter、ツールチップを表示する必要があるかどうかを確認するためだけに確認する必要があるような状況でのみ実行することで相殺できます。
Jacob

5

イタロからの回答がとてもいいです!ただし、少し調整してみましょう。

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

ブラウザ間の互換性

実際に、上記のコードを試してとconsole.logの値を出力するe.offsetWidthe.scrollWidth、IEで、テキストの切り捨てがない場合でも、1pxまたはの値の違いが発生すること2pxがわかります。

したがって、使用するフォントサイズに応じて、ある程度の許容範囲を設けてください。


IE10では機能しません。offsetWidthはscrollWidthと同じで、どちらも切り捨てられた幅を示します。
SsjCosty 2017年

1
Chrome 60(最新)にもこの許容範囲が必要です。
JanŻankowski2017


4

このサンプルは、テキストが切り捨てられたセルテーブルのツールチップを示しています。テーブルの幅に基づいて動的です:

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

デモ:https : //jsfiddle.net/t4qs3tqs/

jQueryのすべてのバージョンで動作します


1

私はそれを検出するより良い方法は使用だと思いますgetClientRects()、それは各長方形が同じ高さを持っているようですので、異なるtop値の数で行番号を計算できます。

getClientRectsこのように働く

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

getRowRectsこのように働く

このように検出できます


1

すべてのソリューションが実際には機能しませんでした。機能したのは、要素をその親(またはトリガー、要素に応じて子)のと比較することでしたscrollWidthscrollWidth

scrollWidthが親よりも高い場合、.text-ellipsisアクティブであることを意味します。


event親要素はいつですか

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

event子要素はいつですか

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

0

e.offsetWidth < e.scrollWidth解決策は、常に動作していません。

純粋なJavaScriptを使用したい場合は、これを使用することをお勧めします。

(タイプスクリプト)

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

ソリューション@ItaloBorssattoは完璧です。しかし、SOを見る前に、私は私の決断をしました。ここにあります :)

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

私の実装)

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>


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.