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/…
Адриен Бе
7
Примечание для потомков: text-overflow:ellipsis;теперь работает в Firefox; он был добавлен в Firefox 7 (выпущен в сентябре 2011 года).
Слеське

Ответы:

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;
}
Snæbjørn
источник
18
Это не так хорошо, как всплывающая подсказка, потому что это может нарушить макет.
Александр
2
Проблема с подсказками в том, что они не работают на мобильных устройствах, по крайней мере, на данный момент. Этот ответ может (читай: вероятно, будет) нарушить компоновку, но его можно адаптировать, например, для другого цвета фона для элемента наведения. Это все еще не будет работать на мобильных устройствах, но это еще один вариант для настольных компьютеров.
Trysis
1
Спасибо за этот великолепный ответ, который отлично отвечает моим потребностям. Используя некоторый JavaScript, мы могли бы представить, как сделать блок абсолютно позиционированным, чтобы он не нарушал компоновку.
Niavlys
2
Это хороший ответ, лучше, чем вздутие живота JavaScript по вопросу CSS. Очевидно, что многоточие ДОЛЖНО иметь автоматическую опцию заголовка, способ, которым она реализована в данный момент, лишен смысла. Жаль, что css не позволяет: hover, если последние символы '...'
John
32

Ответ uosɐſ в основном верен, но вы, вероятно, не хотите делать это в событии mouseenter. Это заставит его выполнять вычисления, чтобы определить, нужно ли это, каждый раз, когда вы наводите курсор мыши на элемент. Если размер элемента не меняется, нет причин делать это.

Было бы лучше просто вызвать этот код сразу после добавления элемента в 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());
});
Elezar
источник
+1, делать это при загрузке страницы кажется проще, но также предоставляет больше возможностей. то есть. Вы можете захотеть стилизовать этот элемент, который имеет всплывающую подсказку с пунктирной линией, чтобы «сигнализировать» подсказку. такие как$(".mightOverflow").each(function() { if( $(this).offsetWidth < $(this).scrollWidth && !$(this).attr('title')){ $(this).attr('title', $(this).text()); $(this).css('border-bottom', '1px dotted #A8A8A8'); } });
Адриен Бе
«каждый раз, когда вы наводите курсор мыши» неправильно (теперь, по крайней мере, я не знаю, был ли обновлен ответ). Он проверяет, был ли он рассчитан ранее. &&! $ this.attr ('title') '
Rune Jeppesen
Нет, это просто мешает добавить атрибут title снова. Расчет , чтобы определить , если это необходимо еще сделать. this.offsetWidth < this.scrollWidthи, возможно, даже проверка !$this.attr('title')будет выполняться каждый раз, когда вы наводите курсор мыши на элемент.
Елезар
9
Проблема этого метода заключается в том, что все элементы проверяются одновременно (потенциальное влияние на производительность), и он рассчитывается только один раз, поэтому, если пользователь сжимает браузер, вызывая усечение или расширение элементов, приводя к тому, что они больше не усекаются, вы не можете обновить наличие всплывающей подсказки. Присоединение вашего кода к изменению размера окна может привести к снижению производительности, поскольку каждый элемент проверяет свой размер. Используя делегирование события "$ (document) .on ('mouseenter', '.mightOverflow', ..." и откладывая проверку до тех пор, пока вы не наведете курсор мыши на элемент, вы можете обновлять его на лету и проверять только 1 элемент @ время
Крис Янссен
Это не работает на 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>

Ханнес
источник
Это именно то, что я искал!
Пранит Надкар
Отличное решение, именно то, что мне нужно. Спасибо !
Агу 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': 'visible', '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());
});
fanfavorite
источник
Почему отрицательный голос? Пожалуйста, прокомментируйте, если вы делаете это, чтобы люди знали, в чем проблема. Спасибо.
Фанфаворит
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. Почему бы не проверить, больше ли ширина строки? Если вы хотите сделать что-то действительно прикольное, вы можете добавить скрытый элемент без переполнения текста: установите многоточие и сравните ширину обоих. Если скрытый шире, чем не скрытый, добавьте атрибут title к видимому.
Марк Костелло
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()
Basheer AL-MOMANI
2

Вот решение Vanilla JavaScript:

(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>

wuarmin
источник
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
источник
Привет @FarukT Ваше решение работает хорошо для меня, большое спасибо. Я хочу спросить, есть ли у меня два html-тега, предположим input & span, и я хочу рассчитать offsetWidth обоих этих тегов одновременно, чтобы выполнить некоторые арифметические вычисления с использованием этих offsetWidth. Как бы я это сделал?
Кунал Тяги
Привет @KunalTyagi, вы можете сделать это с двумя отдельными функциями, я написал только одну с входом и диапазоном, но вы можете повторить эту функцию 2 раза, например, первая функция только с входом, а во второй функции только диапазон. вот так: $ (document) .on ('mouseover', 'input', function () {...}); и второй $ (document) .on ('mouseover', '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());
            }
        }
    });
}
Dexygen
источник