HTML - jak mogę wyświetlać podpowiedzi TYLKO, gdy aktywna jest elipsa

205

Mam zakres z dynamicznymi danymi na mojej stronie, ze ellipsisstylem.

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

Chciałbym dodać do tego elementu etykietę narzędzia o tej samej treści, ale chcę, aby pojawiała się tylko wtedy, gdy zawartość jest długa, a na ekranie pojawia się wielokropek.

Czy jest na to jakiś sposób?
Czy przeglądarka generuje zdarzenie, gdy ellipsisjest aktywowane?

* Przeglądarka: Internet Explorer

Człowiek Pająk
źródło
1
Pamiętaj, że w text-overflow:ellipsis;ogóle nie działa w Firefoksie - zobacz to pytanie, aby uzyskać więcej informacji: stackoverflow.com/questions/4927257/…
Spudley
2
Po prostu musiałem zrobić coś podobnego. Sprawdzanie, czy element.offsetWidth < element.scrollWidthzgodnie z tą odpowiedzią wydaje się do tej pory działać.
Martin Smith
wykrywanie elipsy: stackoverflow.com/questions/7738117/…
Adrien Be
7
Uwaga dla potomnych: text-overflow:ellipsis;teraz działa w przeglądarce Firefox; został dodany w Firefox 7 (wydany we wrześniu 2011 r.).
śleske

Odpowiedzi:

161

Oto sposób, w jaki robi to za pomocą wbudowanego ustawienia wielokropka i dodaje titleatrybut na żądanie (z jQuery) w oparciu o komentarz Martina Smitha:

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

    if(this.offsetWidth < this.scrollWidth && !$this.attr('title')){
        $this.attr('title', $this.text());
    }
});
Jason Kleban
źródło
4
Dzięki, działa jak urok! Jeśli jednak chcesz uczynić go bardziej wydajnym, możesz rozważyć zamianę bind () na on () w taki sposób: $ (document) .on ('mouseenter', '.mightOverflow', function () {...});
Ziad
17
Jeśli chcesz, aby podpowiedź była automatycznie usuwana, jeśli tekst nie jest już przepełniony, zmień na: $ (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')}});
Chris Janssen
1
mouseenter to wydarzenie, z którym się łączymy lub nasłuchujemy. Tak naprawdę nie musimy dodawać podpowiedzi do elementów, dopóki ktoś się nad nimi nie najedzie. Dlatego odraczamy dodawanie do tego momentu myszy na dowolnym elemencie DOM za pomocą klasy „może przepełnić”. Podpowiedzi Just-in-Time
Jason Kleban
2
"As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document"Aby uzyskać więcej informacji, patrz api.jquery.com/bind i api.jquery.com/on
Adrien Be
2
Nie działa na IE10 - offsetWidth jest identyczny z scrollWidth, oba dają mi obciętą szerokość.
SsjCosty
59

Oto czyste rozwiązanie CSS. Nie potrzebujesz jQuery. Nie będzie wyświetlać podpowiedzi, zamiast tego powiększy zawartość do pełnej długości po najechaniu myszką.

Działa świetnie, jeśli masz treści, które zostaną zastąpione. Nie musisz wtedy uruchamiać funkcji jQuery za każdym razem.

.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
źródło
18
Nie jest tak dobra jak podpowiedź, ponieważ może uszkodzić układ.
Alexander
2
Problem z podpowiedziami polega na tym, że przynajmniej nie działają na urządzeniach mobilnych. Ta odpowiedź mogłaby (czytaj: prawdopodobnie zepsuje) układ, ale może być na przykład dostosowana do innego koloru tła dla zawieszonego elementu. To nadal nie działałoby na urządzeniach mobilnych, ale jest to kolejna opcja na komputery.
trysis
1
Dzięki za świetną odpowiedź, która idealnie pokrywa moje potrzeby. Używając JavaScript, możemy sobie wyobrazić, aby blok był absolutnie ustawiony tak, aby nie łamał układu.
Niavlys,
2
To ładna odpowiedź, lepsza niż wzdęcie javascript w kwestii css. Najwyraźniej elipsa POWINNA mieć automatyczną opcję tytułu, ponieważ sposób, w jaki jest obecnie wdrażany, nie ma sensu. Szkoda, że ​​css nie pozwala tylko: najechać kursorem, jeśli ostatnimi znakami są „...”
John
32

Odpowiedź uosɐſ jest zasadniczo poprawna, ale prawdopodobnie nie chcesz tego robić w przypadku zdarzenia w centrum myszy. Spowoduje to, że wykona obliczenia, aby określić, czy jest ono potrzebne, za każdym razem, gdy najedziesz myszką na element. O ile rozmiar elementu się nie zmieni, nie ma powodu, aby to robić.

Lepiej byłoby po prostu wywołać ten kod natychmiast po dodaniu elementu do DOM:

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

Lub, jeśli nie wiesz, kiedy dokładnie został dodany, wywołaj ten kod po zakończeniu ładowania strony.

jeśli masz więcej niż jeden element, z którym musisz to zrobić, możesz nadać im tę samą klasę (np. „mayOverflow”) i użyć tego kodu, aby zaktualizować je wszystkie:

$('.mightOverflow').each(function() {
    var $ele = $(this);
    if (this.offsetWidth < this.scrollWidth)
        $ele.attr('title', $ele.text());
});
Elezar
źródło
+1, robienie tego przy ładowaniu strony wydaje się prostsze, ale daje też więcej możliwości. to znaczy. możesz stylizować ten element, który ma etykietkę z kropkowanym podkreśleniem, aby „zasygnalizować” etykietkę. takie jak$(".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
„za każdym razem, gdy najedziesz myszką” jest błędne (przynajmniej teraz - nie wiem, czy odpowiedź została zaktualizowana) Sprawdza, czy została wcześniej obliczona „&&! $ this.attr („ title ”)”
Rune Jeppesen
Nie, to po prostu powstrzymuje go przed ponownym dodaniem atrybutu tytułu. Obliczenie aby ustalić, czy jest to potrzebne jest jeszcze zrobione. this.offsetWidth < this.scrollWidthi być może nawet sprawdzenie !$this.attr('title')zostanie wykonane za każdym razem, gdy najedziesz myszką na element.
Elezar,
9
Problem z tą metodą polega na tym, że wszystkie elementy są sprawdzane jednocześnie (potencjalny wpływ na wydajność) i oblicza się tylko raz, więc jeśli użytkownik zmniejszy przeglądarkę, powodując obcięcie lub rozszerzenie, powodując, że przestaną być obcięte, nie można zaktualizuj obecność podpowiedzi. Ponowne dołączenie kodu do zmiany rozmiaru okna miałoby problem z wydajnością, ponieważ każdy element sprawdza jego rozmiar. Korzystając z delegowania zdarzeń „$ (dokument) .on („ mouseenter ”,„ .mightOverflow ”,…)” i opóźniając sprawdzenie do momentu najechania kursorem na element, możesz aktualizować w locie i sprawdzać tylko 1 element @ na raz
Chris Janssen
Nie działa na IE10 - offsetWidth jest identyczny z scrollWidth, oba dają mi obciętą szerokość.
SsjCosty
18

Oto dwa inne rozwiązania oparte wyłącznie na CSS:

  1. Pokaż obcięty tekst w miejscu:

.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. Pokaż dowolną „etykietkę” :

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

Hannes
źródło
Właśnie tego szukałem!
Praneet Nadkar
Doskonałe rozwiązanie, dokładnie to, czego potrzebuję. Dziękuję Ci !
Agu V
17

Oto moja wtyczka 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);

Stosowanie:

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

Edytować:

Zrobiłem sedno dla tej wtyczki. https://gist.github.com/UziTech/d45102cdffb1039d4415

Tony Brix
źródło
1
Nie działa na IE10 - offsetWidth jest identyczny z scrollWidth, oba dają mi obciętą szerokość.
SsjCosty
@SsjCosty dlaczego testujesz na IE 10? W tym momencie nie powinno być nikogo używającego IE 10, ponieważ każdy, kto może zainstalować IE 10, może zainstalować IE 11.
Tony Brix
No cóż, mamy politykę firmy, w której obsługiwana jest nasza minimalna wersja IE 10. Również dlatego, że wielu naszych klientów (niektóre banki i różne instytucje) nadal używa IE10. No cóż.
SsjCosty
12

Musimy wykryć, czy elipsa jest rzeczywiście zastosowana, a następnie pokazać etykietkę narzędzia, aby odsłonić pełny tekst. Nie wystarczy tylko porównywać „ this.offsetWidth < this.scrollWidth”, gdy element prawie trzyma zawartość, ale brakuje tylko jednego lub dwóch dodatkowych pikseli szerokości, szczególnie dla tekstu chińskich / japońskich / koreańskich znaków o pełnej szerokości.

Oto przykład: http://jsfiddle.net/28r5D/5/

Znalazłem sposób, aby poprawić wykrywanie elipsy:

  1. this.offsetWidth < this.scrollWidthNajpierw porównaj „ ”, kontynuuj krok 2, jeśli się nie powiedzie.
  2. Tymczasowo przełącz styl css na {„przepełnienie”: „widoczne”, „biała spacja”: „normalny”, „podział słów”: „wszystkie”.
  3. Pozwól przeglądarce dokonać przekazywania. Jeśli dzieje się zawijanie słów, element powiększy swoją wysokość, co oznacza również, że wymagana jest elipsa.
  4. Przywróć styl css.

Oto moje ulepszenie: http://jsfiddle.net/28r5D/6/

Feng Dihai
źródło
10

Stworzyłem wtyczkę jQuery, która używa podpowiedzi Bootstrap zamiast podpowiedzi przeglądarki. Pamiętaj, że nie zostało to przetestowane ze starszą przeglądarką.

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");
      }
    }
  });
};
Steven
źródło
Prosty i genialny. Dziękuję Ci!
GumZ
3

Oto co zrobiłem. Większość skryptów podpowiedzi wymaga wykonania funkcji przechowującej podpowiedzi. To jest przykład 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();
});

Jeśli nie chcesz sprawdzać ellipsis css, możesz uprościć:

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

Mam wokół niego „kiedy”, więc funkcja „setupTooltip” nie działa, dopóki wszystkie tytuły nie zostaną zaktualizowane. Zamień „setupTooltip” na funkcję podpowiedzi, a * na elementy, które chcesz sprawdzić. * przejdzie je wszystkie, jeśli go opuścisz.

Jeśli chcesz po prostu zaktualizować atrybuty tytułu za pomocą podpowiedzi przeglądarki, możesz uprościć:

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

Lub bez sprawdzania elipsy:

$.when($('*').filter(function() {
   return (this.offsetWidth < this.scrollWidth && !$(this).attr('title'));
}).each(function() {
   $(this).attr('title', $(this).text());
});
fanfavorite
źródło
Dlaczego głosowanie negatywne? Jeśli to robisz, proszę o komentarz, aby ludzie wiedzieli, na czym polega problem. Dzięki.
fanfavorite
2

Jeśli chcesz to zrobić wyłącznie przy użyciu javascript, zrobiłbym następujące. Nadaj zakresowi atrybut id (aby można go było łatwo pobrać z DOM) i umieść całą zawartość w atrybucie o nazwie „content”:

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

Następnie w javascript możesz wykonać następujące czynności po wstawieniu elementu do 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); 
}

Oczywiście, jeśli używasz javascript do wstawiania zakresu do DOM, możesz po prostu zachować zawartość w zmiennej przed wstawieniem. W ten sposób nie potrzebujesz atrybutu treści w zakresie.

Są bardziej eleganckie rozwiązania, jeśli chcesz używać jQuery.

Mark Costello
źródło
Miły. Ponieważ używam JQuery w innej części tej strony - jakie jest eleganckie rozwiązanie JQuery?
Spiderman
$("#myDataId").attr("title", function() { var innerText = $(this).text(); var content = $(this).attr("content"); if (innerText.length <= content.length) { return content; } return null; });
Mark Costello
1
Wypróbowałem twoją pierwszą sugestię z czystym javascript - to nie działa. właściwość „innerText” zapisuje pełną długość tekstu, nawet jeśli nie wyświetla się on całkowicie na ekranie, więc zawsze: elemInnerText.length == elemContent.length !!
Spiderman
Styl twojej rozpiętości to szerokość 71. Dlaczego nie sprawdzić, czy szerokość łańcucha jest dłuższa? Jeśli chcesz zrobić coś naprawdę funky, możesz dodać ukryty element bez ustawionego przepełnienia tekstu: elipsa i porównać szerokości obu. Jeśli ukryty jest szerszy niż nie ukryty, dodaj atrybut tytułu do widocznego.
Mark Costello
2

Mam klasę CSS, która określa, gdzie umieścić elipsę. Na tej podstawie robię następujące (zestaw elementów może być inny, piszę te, w których używana jest elipsa, oczywiście może to być osobny selektor klasy):

$(document).on('mouseover', 'input, td, th', function() {
    if ($(this).css('text-overflow') && typeof $(this).attr('title') === 'undefined') {
        $(this).attr('title', $(this).val());
    }
});
Alexander
źródło
1
i mój przypadek thisbył anchorwięc, więc użyłem this.text()zamiastval()
Basheer AL-MOMANI
2

Oto rozwiązanie 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
źródło
1

To było moje rozwiązanie, działa jak urok!

    $(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
źródło
Cześć @ FarukT Twoje rozwiązanie działa dla mnie dobrze, dziękuję bardzo. Chcę zapytać, czy mam dwa znaczniki html, załóżmy, że input & span i chcę obliczyć offsetWidth obu tych znaczników jednocześnie, aby wykonać obliczenia arytmetyczne przy użyciu tych offsetWidth. Jak mam to zrobić?
Kunal Tyagi
Cześć @ KunalTyagi, możesz to zrobić z 2 oddzielnymi funkcjami, napisałem tylko jedną z wejściem i zakresem, ale możesz replikować tę funkcję 2 razy, na przykład pierwsza funkcja tylko z wejściem, a w drugiej funkcji tylko zakres. jak to: $ (dokument) .on ('mouseover', 'input', function () {...}); a drugi $ (dokument) .on ('mouseover', 'span', function () {...});
FarukT
0

Żadne z powyższych rozwiązań nie działało dla mnie, ale wymyśliłem świetne rozwiązanie. Największym błędem, jaki popełniają ludzie, jest zadeklarowanie wszystkich 3 właściwości CSS w elemencie podczas ładowania strony. Musisz dodać te style + etykietkę dynamicznie JEŻELI i TYLKO JEŚLI zakres, na którym chcesz elips, jest szerszy niż jego element nadrzędny.

    $('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'
            });
        }
    });
Brian Gabriel
źródło
0

Możesz ewentualnie otoczyć przęsło innym przęsłem, a następnie po prostu sprawdź, czy szerokość pierwotnego / wewnętrznego przęsła jest większa niż szerokość nowego / zewnętrznego przęsła. Zauważ, że mówię, że możliwe - jest to mniej więcej oparte na mojej sytuacji, w której miałem spanwnętrze, tdwięc nie wiem, czy to będzie działać z spanwnętrzem span.

Oto mój kod dla innych, którzy mogą znaleźć się w pozycji podobnej do mojej; Kopiuję / wklejam go bez modyfikacji, nawet jeśli jest to w kontekście Angular, nie sądzę, że szkodzi to czytelności i podstawowej koncepcji. Zakodowałem to jako metodę usługi, ponieważ musiałem zastosować go w więcej niż jednym miejscu. Selektor, który przekazałem, był selektorem klasy, który będzie pasował do wielu instancji.

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
źródło