HTML - як я можу показувати підказку ТІЛЬКИ, коли активовано еліпсис


205

У мене на моїй сторінці проміжок із динамічними даними на ellipsisстилі.

.my-class
{
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;  
  width: 71px;
}
<span id="myId" class="my-class"></span>
document.getElementById('myId').innerText = "...";

Я хотів би додати до цього елемента підказку з тим самим вмістом, але я хочу, щоб він відображався лише тоді, коли вміст довгий і на екрані з’являється еліпсис.

Чи можна це зробити?
Чи переглядає браузер подію, коли ellipsisвона активована?

* Браузер: Internet Explorer


1
Майте на увазі , що text-overflow:ellipsis;не працює взагалі в Firefox - см це питання докладніше: stackoverflow.com/questions/4927257 / ...
Spudley

2
Мені просто довелося зробити щось подібне. Перевірка того, чи element.offsetWidth < element.scrollWidthвідповідає ця відповідь , поки що працює.
Мартін Сміт

виявлення еліпсису: stackoverflow.com/questions/7738117/…
Adrien Be

7
Примітка для нащадків: text-overflow:ellipsis;зараз працює у Firefox; він був доданий у Firefox 7 (вийшов у вересні 2011 року).
sleske

Відповіді:


161

Ось спосіб, як це зробити, використовуючи вбудований параметр еліпсису, і додає titleатрибут на вимогу (з jQuery), будуючи коментар Мартіна Сміта:

$('.mightOverflow').bind('mouseenter', function(){
    var $this = $(this);

    if(this.offsetWidth < this.scrollWidth && !$this.attr('title')){
        $this.attr('title', $this.text());
    }
});

4
Дякую, працює як шарм! Однак якщо ви хочете зробити це більш ефективним, ви можете розглянути можливість заміни bind () на on (), наприклад: $ (document) .on ('mouseenter', '.mightOverflow', function () {...});
Зіад

17
Якщо ви хочете, щоб підказка автоматично видалялася, якщо текст більше не переповнюється, змініть на: $ (document) .on ('mouseenter', '.mightOverflow', function () {var $ t = $ (this); var title = $ t.attr ('title'); if (! title) {if (this.offsetWidth <this.scrollWidth) $ t.attr ('title', $ t.text ())} else {if (this.offsetWidth> = this.scrollWidth && title == $ t.text ()) $ t.removeAttr ('title')}});
Кріс Янссен

1
mouseenter - це подія, яку ми пов'язуємо або слухаємо. Нам фактично не потрібно додавати підказки до елементів, поки хтось насправді не наводить на них курсор. Отже, ми відкладаємо додавання до цієї точки мишіцентру на будь-якому з елементів DOM з класом "mightoverflow". Підказки, що знаходяться вчасно
Джейсон Клебан

2
"As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document"Докладніше див. api.jquery.com/bind та api.jquery.com/on
Adrien Be

2
Він не працює на IE10 - offsetWidth ідентичний ширині scrollWidth, обидва дають мені усічену ширину.
SsjCosty

59

Ось чисте рішення CSS. Не потрібно jQuery. Він не відображатиметься підказка, замість цього він просто розширить вміст на повну довжину в режимі миші.

Чудово працює, якщо у вас є вміст, який буде замінено. Тоді вам не доведеться щоразу виконувати функцію jQuery.

.might-overflow {
    text-overflow: ellipsis;
    overflow : hidden;
    white-space: nowrap;
}

.might-overflow:hover {
    text-overflow: clip;
    white-space: normal;
    word-break: break-all;
}

18
Це не так добре, як підказка, тому що може порушити макет.
Олександр

2
Проблема з підказками полягає в тому, що вони не працюють на мобільних пристроях, принаймні поки що. Ця відповідь могла (прочитати: напевно, буде) порушити макет, але вона могла бути адаптована до, наприклад, мати інший колір фону для зависного елемента. Це все ще не працює на мобільних пристроях, але це ще один варіант для робочого столу.
trysis

1
Дякую за чудову відповідь, яка чудово відповідає моїм потребам. Використовуючи деякі JavaScript, ми могли б уявити, як зробити блок абсолютно позиціонованим, щоб він не порушував макет.
Niavlys

2
Це приємна відповідь, краще, ніж роздуття JavaScript на проблемі css. Очевидно, що еліпсис ПОТРІБНО матиме автоматизовану опцію заголовка, тому, як це реалізовано на даний момент, бракує сенсу. Забагато css не дозволяє лише: навести курсор, якщо останні символи є "..."
Джон

32

Відповідь uosɐſ принципово правильна, але ви, мабуть, не хочете цього робити у випадку події з мишею. Це призведе до того, щоб він зробив обчислення, щоб визначити, чи потрібен він кожен раз, коли ви клацаєте мишкою на елемент. Якщо розмір елемента не змінюється, немає причин для цього.

Було б краще просто зателефонувати цьому коду відразу ж після додавання елемента до DOM:

var $ele = $('#mightOverflow');
var ele = $ele.eq(0);
if (ele.offsetWidth < ele.scrollWidth)
    $ele.attr('title', $ele.text());

Або, якщо ви не знаєте, коли саме він доданий, то зателефонуйте до цього коду після завантаження сторінки.

якщо у вас є більше одного елемента, з яким вам потрібно це зробити, ви можете дати їм все той же клас (наприклад, "mightOverflow") і використовувати цей код для оновлення всіх:

$('.mightOverflow').each(function() {
    var $ele = $(this);
    if (this.offsetWidth < this.scrollWidth)
        $ele.attr('title', $ele.text());
});

+1, зробити це під час завантаження сторінки здається більш простим, але також надає більше можливостей. тобто. ви можете стилізувати цей елемент, який має підказку з пунктирним підкресленням, щоб "сигналізувати" підказку. наприклад,$(".mightOverflow").each(function() { if( $(this).offsetWidth < $(this).scrollWidth && !$(this).attr('title')){ $(this).attr('title', $(this).text()); $(this).css('border-bottom', '1px dotted #A8A8A8'); } });
Adrien Be

"кожен раз, коли ви клацаєте мишкою" помиляється (принаймні, зараз - я не знаю, чи відповідь було оновлено). Це перевіряє, чи було раніше обчислено "&&! $ this.attr (" назва ")
Руна Джеппенсен

Ні, це просто заважає знову додавати атрибут заголовка. Розрахунок , щоб визначити , якщо це необхідно ще зробити. this.offsetWidth < this.scrollWidthі, можливо, навіть перевірка на !$this.attr('title')предмет буде виконуватися щоразу, коли ви кладете на елемент миші.
Елезар

9
Проблема цього методу полягає в тому, що всі елементи перевіряються одразу (потенційний вплив на продуктивність), і він обчислюється лише один раз, так що якщо користувач скорочує браузер, внаслідок чого речі обрізаються або розширюються, внаслідок чого вони більше не врізаються, ви не можете оновити наявність підказки. Якщо приєднати код знову до розміру вікон, виникне проблема з продуктивністю, оскільки кожен елемент перевіряє його розмір. Використовуючи делегування події "$ (document) .on ('mouseenter', '.mightOverflow', ..." і затягуючи чек, поки елемент миші не переміститься, ви можете оновити на льоту і перевірити лише 1 елемент @ a time
Кріс Янссен

Він не працює на IE10 - offsetWidth ідентичний ширині scrollWidth, обидва дають мені усічену ширину.
SsjCosty

18

Ось два інших чисті CSS рішення:

  1. Покажіть усічений текст на місці:

.overflow {
  overflow: hidden;
  -ms-text-overflow: ellipsis;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.overflow:hover {
  overflow: visible;
}

.overflow:hover span {
  position: relative;
  background-color: white;

  box-shadow: 0 0 4px 0 black;
  border-radius: 1px;
}
<div>
  <span class="overflow" style="float: left; width: 50px">
    <span>Long text that might overflow.</span>
  </span>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad recusandae perspiciatis accusantium quas aut explicabo ab. Doloremque quam eos, alias dolore, iusto pariatur earum, ullam, quidem dolores deleniti perspiciatis omnis.
</div>

  1. Показати довільну "підказку" :

.wrap {
  position: relative;
}

.overflow {
  white-space: nowrap; 
  overflow: hidden;
  text-overflow: ellipsis;
  
  pointer-events:none;
}

.overflow:after {
  content:"";
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  width: 20px;
  height: 15px;
  z-index: 1;
  border: 1px solid red; /* for visualization only */
  pointer-events:initial;

}

.overflow:hover:after{
  cursor: pointer;
}

.tooltip {
  /* visibility: hidden; */
  display: none;
  position: absolute;
  top: 10;
  left: 0;
  background-color: #fff;
  padding: 10px;
  -webkit-box-shadow: 0 0 50px 0 rgba(0,0,0,0.3);
  opacity: 0;
  transition: opacity 0.5s ease;
}


.overflow:hover + .tooltip {
  /*visibility: visible; */
  display: initial;
  transition: opacity 0.5s ease;
  opacity: 1;
}
<div>
  <span class="wrap">
    <span class="overflow" style="float: left; width: 50px">Long text that might overflow</span>
    <span class='tooltip'>Long text that might overflow.</span>
  </span>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad recusandae perspiciatis accusantium quas aut explicabo ab. Doloremque quam eos, alias dolore, iusto pariatur earum, ullam, quidem dolores deleniti perspiciatis omnis.
</div>


Це саме те, що я шукав!
Praneet Nadkar

Відмінне рішення, саме те, що мені потрібно. Дякую !
Агу V

17

Ось мій плагін jQuery:

(function($) {
    'use strict';
    $.fn.tooltipOnOverflow = function() {
        $(this).on("mouseenter", function() {
            if (this.offsetWidth < this.scrollWidth) {
                $(this).attr('title', $(this).text());
            } else {
                $(this).removeAttr("title");
            }
        });
    };
})(jQuery);

Використання:

$("td, th").tooltipOnOverflow();

Редагувати:

Я створив суть цього плагіна. https://gist.github.com/UziTech/d45102cdffb1039d4415


1
Він не працює на IE10 - offsetWidth ідентичний ширині scrollWidth, обидва дають мені усічену ширину.
SsjCosty

@SsjCosty Чому ви тестуєте IE 10? На даний момент ніхто не використовує IE 10, оскільки кожен, хто може встановити IE 10, може встановити IE 11.
Тоні Брікс,

Ну добре, що ми маємо політику компанії, що підтримується наша мінімальна версія IE - 10. Також багато наших клієнтів (деякі банки та різні установи) все ще використовують IE10. Що ж, добре.
SsjCosty

12

Нам потрібно виявити, чи дійсно застосовано еліпсис, а потім показати підказку для розкриття повного тексту. Недостатньо лише порівняти " this.offsetWidth < this.scrollWidth", коли елемент майже не містить вміст, але лише йому бракує ще одного або двох пікселів у ширину, особливо для тексту китайських / японських / корейських символів на повну ширину.

Ось приклад: http://jsfiddle.net/28r5D/5/

Я знайшов спосіб покращити виявлення еліпсису:

  1. Порівняйте this.offsetWidth < this.scrollWidthспочатку " ", якщо не вдалося продовжити крок №2.
  2. Тимчасово переключіть стиль css на {'overflow': 'vid', 'white-space': 'normal', 'word-break': 'break-all'}.
  3. Нехай браузер робить ретрансляцію. Якщо відбувається обгортання слів, елемент збільшить свою висоту, а це означає, що необхідний еліпсис.
  4. Відновити стиль css.

Ось моє вдосконалення: http://jsfiddle.net/28r5D/6/


10

Я створив плагін jQuery, який використовує підказку Bootstrap замість вбудованої підказки браузера. Зверніть увагу, що це не перевірено у старшому браузері.

JSFiddle: https://jsfiddle.net/0bhsoavy/4/

$.fn.tooltipOnOverflow = function(options) {
    $(this).on("mouseenter", function() {
    if (this.offsetWidth < this.scrollWidth) {
        options = options || { placement: "auto"}
        options.title = $(this).text();
      $(this).tooltip(options);
      $(this).tooltip("show");
    } else {
      if ($(this).data("bs.tooltip")) {
        $tooltip.tooltip("hide");
        $tooltip.removeData("bs.tooltip");
      }
    }
  });
};

Простий і блискучий. Дякую!
GumZ

3

Це я і зробив. Більшість скриптів підказок вимагають виконання функції, яка зберігає підказки. Це приклад jQuery:

$.when($('*').filter(function() {
   return $(this).css('text-overflow') == 'ellipsis';
}).each(function() {
   if (this.offsetWidth < this.scrollWidth && !$(this).attr('title')) {
      $(this).attr('title', $(this).text());
   }
})).done(function(){ 
   setupTooltip();
});

Якщо ви не хочете перевіряти еліпсис css, ви можете спростити:

$.when($('*').filter(function() {
   return (this.offsetWidth < this.scrollWidth && !$(this).attr('title'));
}).each(function() {
   $(this).attr('title', $(this).text());
})).done(function(){ 
   setupTooltip();
});

У мене є "коли" навколо нього, так що функція "setupTooltip" не виконується, поки не будуть оновлені всі заголовки. Замініть "setupTooltip" на функцію підказки та * на елементи, які ви хочете перевірити. * пройде через них усі, якщо ви залишите його.

Якщо ви просто хочете просто оновити атрибути заголовка за допомогою підказки веб-переглядачів, ви можете спростити:

$('*').filter(function() {
   return $(this).css('text-overflow') == 'ellipsis';
}).each(function() {
   if (this.offsetWidth < this.scrollWidth && !$(this).attr('title')) {
      $(this).attr('title', $(this).text());
   }
});

Або без перевірки на еліпсис:

$.when($('*').filter(function() {
   return (this.offsetWidth < this.scrollWidth && !$(this).attr('title'));
}).each(function() {
   $(this).attr('title', $(this).text());
});

Чому потік? Прокоментуйте, будь ласка, якщо ви це робите, щоб люди знали, у чому проблема. Дякую.
фанфаворит

2

Якщо ви хочете зробити це виключно за допомогою javascript, я зробив би наступне. Надайте атрибуту span id (щоб його можна було легко отримати з DOM) та розмістіть увесь вміст в атрибуті з назвою "content":

<span id='myDataId' style='text-overflow: ellipsis; overflow : hidden;
 white-space: nowrap; width: 71;' content='{$myData}'>${myData}</span>

Потім у своєму JavaScript ви можете зробити наступне після введення елемента в DOM.

var elemInnerText, elemContent;
elemInnerText = document.getElementById("myDataId").innerText;
elemContent = document.getElementById("myDataId").getAttribute('content')
if(elemInnerText.length <= elemContent.length)
{
   document.getElementById("myDataId").setAttribute('title', elemContent); 
}

Звичайно, якщо ви використовуєте javascript для вставки інтервалу в DOM, ви можете просто зберегти вміст у змінній, перш ніж вставляти його. Таким чином, вам не потрібен атрибут вмісту на проміжку.

Є більш елегантні рішення, ніж це, якщо ви хочете використовувати jQuery.


Приємно. Оскільки я використовую JQuery в іншій частині цієї сторінки - що таке елегантне рішення JQuery ??
Людина-павук

$("#myDataId").attr("title", function() { var innerText = $(this).text(); var content = $(this).attr("content"); if (innerText.length <= content.length) { return content; } return null; });
Марк Костелло

1
Я спробував вашу першу пропозицію з чистим JavaScript - це не працює. властивість 'innerText' зберігає всю довжину тексту, навіть якщо він не відображається повністю на екрані так завжди: elemInnerText.length == elemContent.length !!
Людина-павук

Стиль вашого діапазону - ширина 71. Чому б не перевірити, чи ширина рядка довша за це? Якщо ви хотіли зробити щось справді фанкі, ви можете додати прихований елемент без переповнення тексту: встановити еліпсис та порівняти ширину обох. Якщо прихований ширший, ніж не прихований, додайте атрибут заголовка до видимого.
Марк Костелло

2

У мене клас CSS, який визначає, куди поставити еліпсис. Виходячи з цього, я роблю наступне (набір елементів може бути іншим, я пишу ті, де використовується еліпсис, звичайно, це може бути окремий вибір класу):

$(document).on('mouseover', 'input, td, th', function() {
    if ($(this).css('text-overflow') && typeof $(this).attr('title') === 'undefined') {
        $(this).attr('title', $(this).val());
    }
});

1
я мій випадок thisбув anchorтаким, що я використовував this.text()замістьval()
Башір AL-MOMANI

2

Ось JavaScript-рішення Vanilla:

(function init() {

  var cells = document.getElementsByClassName("cell");

  for(let index = 0; index < cells.length; ++index) {
    let cell = cells.item(index);
    cell.addEventListener('mouseenter', setTitleIfNecessary, false);
  }

  function setTitleIfNecessary() {
    if(this.offsetWidth < this.scrollWidth) {
      this.setAttribute('title', this.innerHTML);
    }
  }

})();
.cell {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border: 1px;
  border-style: solid;
  width: 120px; 
}
<div class="cell">hello world!</div>
<div class="cell">hello mars! kind regards, world</div>


1

Це було моє рішення, працює як шарм!

    $(document).on('mouseover', 'input, span', function() {
      var needEllipsis = $(this).css('text-overflow') && (this.offsetWidth < this.scrollWidth);
      var hasNotTitleAttr = typeof $(this).attr('title') === 'undefined';
      if (needEllipsis === true) {
          if(hasNotTitleAttr === true){
            $(this).attr('title', $(this).val());
          }
      }
      if(needEllipsis === false && hasNotTitleAttr == false){
        $(this).removeAttr('title');
      }
  });

Привіт @FarukT, твоє рішення працює для мене чудово, дякую так багато. Я хочу запитати, чи є у мене два теги html, припустимо, вхідні та прольотні, і я хочу обчислити offsetWidth обох цих тегів одночасно, щоб зробити деякі арифметичні обчислення, використовуючи ці offsetWidth. Як би я це зробив?
Kunal Tiagi

Привіт @KunalTyagi, ви можете йому з двома окремими функціями, мені записали лише одну з введенням і прольотом, але ви можете повторити цю функцію 2 рази, першим прикладом є перша функція лише з введенням, а в другій функції лише прольотом. ось так: $ (document) .on ('перехід миші', 'введення', функція () {...}); і другий $ (документ) .on ('перехід миші', 'span', function () {...});
FarukT

0

Жодне з вищезазначених рішень не працювало для мене, але я зрозумів, що це чудове рішення. Найбільша помилка, яку роблять люди, - це те, що всі 3 властивості CSS оголошені на елементі при завантаженні сторінки. Ви повинні додати ці стилі + підказку динамічно АБО та ТІЛЬКО, якщо проміжок, на який ви хочете еліпси, ширший від його батьківського.

    $('table').each(function(){
        var content = $(this).find('span').text();
        var span = $(this).find('span');
        var td = $(this).find('td');
        var styles = {
            'text-overflow':'ellipsis',
            'white-space':'nowrap',
            'overflow':'hidden',
            'display':'block',
            'width': 'auto'
        };
        if (span.width() > td.width()){
            span.css(styles)
                .tooltip({
                trigger: 'hover',
                html: true,
                title: content,
                placement: 'bottom'
            });
        }
    });

0

Можна, можливо, оточити проміжок іншим проміжком, а потім просто перевірити, чи ширина вихідного / внутрішнього прольоту більше, ніж ширина нового / зовнішнього. Зауважте, що я кажу, що це можливо - це грунтується на моїй ситуації, коли у мене була spanвнутрішня частина tdтак, що я насправді не знаю, що якщо це буде працювати з spanвнутрішньою частиною span.

Ось, хоча, мій код для інших, хто може опинитися в положенні, подібному моєму; Я копіюю / вставляю його без змін, хоча він знаходиться в кутовому контексті, я не думаю, що це погіршує читабельність та важливу концепцію. Я кодував це як сервісний метод, тому що мені потрібно було застосувати його в більш ніж одному місці. Селектор, який я проходив, був селектором класу, який відповідатиме декільком екземплярам.

CaseService.applyTooltip = function(selector) {
    angular.element(selector).on('mouseenter', function(){
        var td = $(this)
        var span = td.find('span');

        if (!span.attr('tooltip-computed')) {
            //compute just once
            span.attr('tooltip-computed','1');

            if (span.width() > td.width()){
                span.attr('data-toggle','tooltip');
                span.attr('data-placement','right');
                span.attr('title', span.html());
            }
        }
    });
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.