Виявлення еліпсису переливання тексту в 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, який переповнюється та обрізається. Це я намагаюся виявити! Вибачте - я міг би поставити це питання краще, визнаю.
deanoj

О, я забув додати - це потрібно лише працювати над webkit, якщо це допоможе ...
deanoj

Я зробив Ghommey просто, щоб побачити, чи це працює ... це не так.
deanoj

аспект еліпсису не має значення; все, що вам потрібно виявити - це переповнення.
Спудлі

Відповіді:


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.
Якуб Гампл

2
Треба сказати, що після пошуку ЛОТу в Інтернеті та спробувати реалізувати багато рішень, це, безумовно, найнадійніший, який я знайшов. Це рішення не дає різних результатів між браузерами, такими як element.innerWidth або element.OffsetWidth, які мають проблеми при використанні поля або набивання. Чудове рішення, молодець.
Напис

1
Для мене це ніде не працювало. Я не впевнений, як це залежить від CSS (я намагався використовувати його на елементах керування входом, рішення нижче працювали принаймні в Chrome і Firefox (але не в IE11)) ...
Олександр

3
Прекрасне рішення, особливо з використанням псевдоселектора jQuery. Але іноді це може не працювати, оскільки ширина обчислюється неправильно, якщо текст елемента має різний стиль (розмір шрифту, інтервал між літерами, поля внутрішніх елементів). У такому випадку я рекомендую додати елемент клонування до $ this.parent () замість "body". Це дасть точну копію елемента і розрахунки будуть правильними. Все одно дякую
Алекс

286

Спробуйте цю функцію JS, передаючи елемент span як аргумент:

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

10
Це повинна бути відповідь - дуже елегантна та працює на Chrome та IE (9)
Roy 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

Додавши відповідь italo, ви також можете це зробити за допомогою jQuery.

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

Також, як вказував Смокі, ви можете використовувати jQuery externalWidth () замість ширини ().

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

9

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

Клонування / маніпулювання DOM таким чином викликає DOM Reflow (див. Пояснення щодо поповнення 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 (остання версія).
Ян Żankowski

5

elem.offsetWdith VS ele.scrollWidth Ця робота для мене! https://jsfiddle.net/gustavojuan/210to9p1/

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

Не працює ні в поточному Chrome, ні в Firefox. Обидва елементи стають червоними.
xehpuk

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.