Обнаружение многоточия текста при переполнении 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
Не дубликат! Этот вопрос касается одного элемента внутри другого, родительского элемента. Я говорю о тексте внутри одного элемента. В моем случае, SPAN в TD никогда не переполняет TD, текст внутри SPAN переполняется и обрезается. Вот что я пытаюсь обнаружить! Извините - я мог бы поставить этот вопрос лучше, я признаю.
Деаной

О, я забыл добавить - это нужно только для работы над webkit, если это поможет ...
deanoj

Я сделал Ghommey только для того, чтобы посмотреть, сработало ли это ... не сработало.
Деаной

многоточие не имеет значения; все, что вам нужно обнаружить, это переполнен ли он.
Спадли

Ответы:


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
@ Лаури Нет; Усечение CSS не меняет фактический текст в блоке, поэтому содержимое всегда одинаково, независимо от того, усечено оно, видимое или скрытое. По-прежнему нет способа программно получить усеченный текст, если бы он был, вам не нужно было бы сначала клонировать элемент!
Кристиан

1
Кажется, это не сработает в ситуациях, когда их нет white-space: nowrap.
Якуб Хэмпл

2
Я должен сказать, что после поиска LOT в Интернете и попытки реализовать много решений, это, безусловно, самое надежное, которое я нашел. Это решение не дает разных результатов в браузерах, таких как element.innerWidth или element.OffsetWidth, у которых возникают проблемы при использовании полей или отступов. Отличное решение, молодец.
Скрипт

1
Для меня это не сработало нигде. Я не уверен, как это зависит от CSS (я пытался использовать его на элементах управления вводом, приведенные ниже решения работали, по крайней мере, в Chrome и Firefox (но не в IE11)) ...
Александр

3
Отличное решение, особенно с использованием псевдо-селектора jQuery. Но иногда может не работать, потому что ширина рассчитывается неправильно, если текст элемента имеет другой стиль (размер шрифта, межбуквенный интервал, поля внутренних элементов). В этом случае я бы порекомендовал добавить элемент clone в $ this.parent () вместо body. Это даст точную копию элемента и расчеты будут правильными. В любом случае, спасибо
Alex

287

Попробуйте эту функцию JS, передав элемент span в качестве аргумента:

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

10
Это должен быть ответ - очень элегантный и работает на Chrome и IE (9)
Рой Truelove

14
Этот ответ и ответ Алекса не будут работать в IE8; Есть некоторые странные случаи, когда ширина и внешняя ширина прокрутки одинаковы ... но имеет многоточие, а затем некоторые случаи, когда они одинаковы ... и многоточия НЕТ. Другими словами, первое решение является единственным, которое работает во всех браузерах.

3
Для тех, кому нужно понять offsetWidth, scrollWidth и clientWidth, вот очень хорошее объяснение: stackoverflow.com/a/21064102/1815779
Linh Dam

4
Об text-overflow:ellipsisэтом должно бытьe.offsetWidth <= e.scrollWidth
oriadam

4
Они всегда равны друг другу в гибких детях, и, следовательно, не будут работать для них.
Кевин Бил

14

Добавив к ответу итало, вы также можете сделать это с помощью jQuery.

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

Также, как указал Смоки, вы можете использовать jQuery outerWidth () вместо width ().

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

9

Для тех, кто использует (или планирует использовать) принятый ответ от Кристиана Варги, пожалуйста, помните о проблемах производительности.

Клонирование / манипулирование DOM таким образом приводит к переворачиванию DOM (см. Объяснение перекомпоновки DOM ), которое является чрезвычайно ресурсоемким.

Использование решения Кристиана Варги для более 100 элементов на странице вызвало 4-секундную задержку переформатирования, во время которой поток JS заблокирован. Учитывая, что JS является однопоточным, это означает значительную задержку UX для конечного пользователя.

Итало Borssatto в ответ должен быть приемлемым один, это было примерно в 10 раз быстрее , во время моего профилирования.


2
К сожалению, это не работает в каждом сценарии (хотелось бы). А упомянутый здесь удар по производительности можно компенсировать, выполнив его только для ситуации, в которой вам нужно проверить - например, только для того, mouseenterчтобы увидеть, должна ли отображаться подсказка.
Джейкоб

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.offsetWidthи e.scrollWidth, в IE вы заметите, что, даже если у вас нет усечения текста, возникает разница в значениях 1pxили 2px.

Таким образом, в зависимости от размера шрифта, который вы используете, допускайте определенный допуск!


Это не работает на IE10 - offsetWidth идентичен scrollWidth, оба дают мне усеченную ширину.
SsjCosty

1
Мне нужен этот допуск на Chrome 60 (последний) тоже.
Ян Янковский


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

Все решения на самом деле не работает для меня, то , что сделал работа сравнивать элементы scrollWidthс scrollWidthего родителем (или ребенка, в зависимости от того, какой элемент имеет триггер).

Когда ребенок 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>


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.