HTML: come posso mostrare il tooltip SOLO quando sono attivati ​​i puntini di sospensione


205

Ho una spanna con dati dinamici nella mia pagina, con ellipsisstile.

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

Vorrei aggiungere a questo elemento una descrizione comandi con lo stesso contenuto, ma voglio che appaia solo quando il contenuto è lungo e i puntini di sospensione appaiono sullo schermo.

C'è modo di farlo?
Il browser genera un evento quando ellipsisè attivato?

* Browser: Internet Explorer


1
Tenete a mente che text-overflow:ellipsis;non funziona affatto in Firefox - vedere questa domanda per più: stackoverflow.com/questions/4927257/...
Spudley

2
Dovevo solo fare qualcosa di simile. Verifica se element.offsetWidth < element.scrollWidthsecondo questa risposta sembra funzionare finora.
Martin Smith,

rilevazione dei puntini di sospensione: stackoverflow.com/questions/7738117/…
Adrien Be

7
Nota per i posteri: text-overflow:ellipsis;ora funziona in Firefox; è stato aggiunto in Firefox 7 (rilasciato a settembre 2011).
sleske,

Risposte:


161

Ecco un modo che lo fa utilizzando l'impostazione dei puntini di sospensione integrata e aggiunge l' titleattributo on demand (con jQuery) basandosi sul commento di Martin Smith:

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

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

4
Grazie, funziona come un fascino! Tuttavia, se vuoi renderlo più efficiente, potresti considerare di sostituire bind () con on () in questo modo: $ (document) .on ('mouseenter', '.mightOverflow', function () {...});
Ziad

17
Se desideri che la descrizione comandi venga rimossa automaticamente se il testo non è più pieno, modifica in: $ (documento) .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 è l'evento a cui ci leghiamo o ascoltiamo. In realtà non è necessario aggiungere la descrizione comando agli elementi finché qualcuno non esegue il mouse su di essi. Quindi rimandiamo l'aggiunta fino a quel punto del mouseenter su uno qualsiasi degli elementi DOM con la classe "mightoverflow". Descrizione
comando

2
"As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document"per maggiori informazioni vedi api.jquery.com/bind e api.jquery.com/on
Adrien Be

2
Non funziona su IE10 - offsetWidth è identico a scrollWidth, entrambi mi danno la larghezza troncata.
SsjCosty,

59

Ecco una soluzione CSS pura. Non è necessario per jQuery. Non mostrerà una descrizione comandi, invece espanderà il contenuto per tutta la sua lunghezza al passaggio del mouse.

Funziona alla grande se hai contenuti che vengono sostituiti. Quindi non è necessario eseguire una funzione jQuery ogni volta.

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

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

18
Non è buono come tooltip, perché potrebbe interrompere il layout.
Alexander,

2
Il problema con i tooltip è che non funzionano sui dispositivi mobili, almeno per ora. Questa risposta potrebbe (leggi: probabilmente lo farà) interrompere il layout, ma potrebbe essere adattata, ad esempio, per avere un colore di sfondo diverso per l'elemento al passaggio del mouse. Non funzionerebbe ancora sui dispositivi mobili, ma è un'altra opzione per desktop.
trysis

1
Grazie per questa grande risposta che copre perfettamente le mie esigenze. Usando un po 'di JavaScript, potremmo immaginare di rendere il blocco assolutamente posizionato in modo che non rompa il layout.
Niavlys,

2
È una bella risposta, meglio del gonfiore di JavaScript su un problema CSS. Chiaramente i puntini di sospensione DOVREBBERO avere un'opzione di titolo automatizzata, il modo in cui è implementato al momento è privo di senso. Peccato che il CSS non lo consenta solo: passa con il mouse se gli ultimi caratteri sono '...'
John

32

La risposta di uosɐſ è fondamentalmente corretta, ma probabilmente non vorrai farlo nell'evento mouseenter. Questo farà sì che esegua il calcolo per determinare se è necessario, ogni volta che passi il mouse sull'elemento. A meno che la dimensione dell'elemento non cambi, non c'è motivo di farlo.

Sarebbe meglio chiamare questo codice immediatamente dopo aver aggiunto l'elemento al DOM:

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

Oppure, se non sai quando viene aggiunto esattamente, chiama quel codice al termine del caricamento della pagina.

se hai più di un singolo elemento con cui devi farlo, puoi dare loro tutti la stessa classe (come "mightOverflow") e usare questo codice per aggiornarli tutti:

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

+1, farlo al caricamento della pagina sembra più semplice ma offre anche più possibilità. vale a dire. potresti voler modellare questo elemento che ha una descrizione comandi con una sottolineatura tratteggiata per "segnalare" la descrizione comando. come$(".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

'ogni volta che passi con il mouse' è sbagliato (almeno ora - non so se la risposta è stata aggiornata) Controlla se è stata precedentemente calcolata '&&! $ this.attr (' title ')'
Rune Jeppesen

No, ciò impedisce semplicemente di aggiungere nuovamente l'attributo title. Il calcolo per determinare se è ancora necessario. this.offsetWidth < this.scrollWidthe possibilmente anche il controllo per !$this.attr('title')verrà eseguito ogni volta che passi il mouse sull'elemento.
Elezar,

9
Il problema con questo metodo è che tutti gli elementi vengono controllati in una sola volta (potenziale impatto sulle prestazioni) e viene calcolata una sola volta, in modo che se l'utente restringe il browser causando il troncamento o l'espansione delle cose, impedendone il troncamento non è più possibile aggiorna la presenza di una descrizione comandi. Associare nuovamente il codice al ridimensionamento della finestra avrebbe un problema di prestazioni poiché ogni articolo ne controlla le dimensioni. Utilizzando la delega degli eventi "$ (document) .on ('mouseenter', '.mightOverflow', ..." e ritardando il controllo fino al passaggio del mouse sull'elemento, è possibile aggiornare al volo e controllare solo 1 elemento alla volta
Chris Janssen,

Non funziona su IE10 - offsetWidth è identico a scrollWidth, entrambi mi danno la larghezza troncata.
SsjCosty,

18

Ecco altre due soluzioni CSS pure:

  1. Mostra il testo troncato sul posto:

.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. Mostra una "descrizione comandi" arbitraria :

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


Questo e 'esattamente quello che stavo cercando!
Praneet Nadkar,

Ottima soluzione, esattamente quello di cui ho bisogno. Grazie !
Agu V

17

Ecco il mio plugin 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);

Uso:

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

Modificare:

Ho fatto un'idea per questo plugin. https://gist.github.com/UziTech/d45102cdffb1039d4415


1
Non funziona su IE10 - offsetWidth è identico a scrollWidth, entrambi mi danno la larghezza troncata.
SsjCosty,

@SsjCosty perché stai testando su IE 10? A questo punto nessuno dovrebbe usare IE 10, dal momento che chiunque può installare IE 10 può installare IE 11.
Tony Brix,

Ah bene, abbiamo una politica aziendale secondo cui la nostra versione minima di IE supportata è 10. Anche perché molti dei nostri clienti (alcune banche e varie istituzioni) usano ancora IE10. Oh bene.
SsjCosty,

12

Dobbiamo rilevare se i puntini di sospensione sono realmente applicati, quindi mostrare una descrizione comando per rivelare il testo completo. Non è sufficiente confrontando " this.offsetWidth < this.scrollWidth" solo quando l'elemento contiene quasi il suo contenuto ma manca solo uno o due pixel in più di larghezza, specialmente per il testo di caratteri cinesi / giapponesi / coreani a larghezza intera.

Ecco un esempio: http://jsfiddle.net/28r5D/5/

Ho trovato un modo per migliorare il rilevamento dei puntini di sospensione:

  1. Confronta this.offsetWidth < this.scrollWidthprima " ", continua il passaggio 2 se fallito.
  2. Passa lo stile CSS temporaneamente a {'overflow': 'visible', 'white-space': 'normal', 'word-break': 'break-all'}.
  3. Consenti al browser di eseguire il relayout. Se si verifica un ritorno a capo automatico, l'elemento espande la sua altezza, il che significa anche che sono necessarie le ellissi.
  4. Ripristina lo stile CSS.

Ecco il mio miglioramento: http://jsfiddle.net/28r5D/6/


10

Ho creato un plug-in jQuery che utilizza la descrizione comandi di Bootstrap anziché la descrizione comandi incorporata del browser. Si noti che questo non è stato testato con un browser precedente.

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");
      }
    }
  });
};

Semplice e geniale. Grazie!
GumZ,

3

Questo è quello che ho fatto. La maggior parte degli script dei suggerimenti richiede di eseguire una funzione che memorizza i suggerimenti. Questo è un esempio di 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();
});

Se non si desidera verificare la presenza di ellissi css, è possibile semplificare come:

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

Ho il "quando" attorno ad esso, in modo che la funzione "setupTooltip" non venga eseguita fino a quando tutti i titoli non sono stati aggiornati. Sostituisci "setupTooltip", con la tua funzione tooltip e il * con gli elementi che vuoi controllare. * li esaminerà tutti se lo lasci.

Se desideri semplicemente aggiornare gli attributi del titolo con la descrizione del browser, puoi semplificare come:

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

O senza controllo per i puntini di sospensione:

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

Perché il downvote? Si prega di commentare se lo si sta facendo in modo che le persone sappiano qual è il problema. Grazie.
fanfavorite,

2

Se vuoi farlo solo usando JavaScript, farei quanto segue. Assegna allo span un attributo id (in modo che possa essere facilmente recuperato dal DOM) e posiziona tutto il contenuto in un attributo chiamato "content":

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

Quindi, nel tuo javascript, puoi fare quanto segue dopo che l'elemento è stato inserito nel 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); 
}

Naturalmente, se stai utilizzando JavaScript per inserire l'intervallo nel DOM, puoi semplicemente mantenere il contenuto in una variabile prima di inserirlo. In questo modo non è necessario un attributo content sull'intervallo.

Ci sono soluzioni più eleganti di questa se vuoi usare jQuery.


Bello. Dato che sto usando JQuery in un'altra parte di questa pagina - qual è la soluzione elegante di 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
Ho provato il tuo primo suggerimento con javascript puro - non funziona. la proprietà 'innerText' salva la lunghezza completa del testo anche se non viene mostrato completamente sullo schermo, quindi sempre: elemInnerText.length == elemContent.length !!
Spiderman,

Lo stile del tuo span è di larghezza 71. Perché non controllare se la larghezza della stringa è più lunga di quella? Se volessi fare qualcosa di veramente funky, potresti aggiungere un elemento nascosto senza l'overflow del testo: impostare i puntini di sospensione e confrontare le larghezze di entrambi. Se quello nascosto è più largo di quello non nascosto, aggiungi un attributo del titolo a quello visibile.
Mark Costello,

2

Ho una classe CSS, che determina dove posizionare i puntini di sospensione. Sulla base di ciò, faccio quanto segue (l'insieme di elementi potrebbe essere diverso, scrivo quelli, in cui vengono utilizzati i puntini di sospensione, ovviamente potrebbe essere un selettore di classe separato):

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

1
il mio caso thisera un anchorcosì che ho usato this.text()invece dival()
Basheer AL-MOMANI

2

Ecco una soluzione 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

Questa era la mia soluzione, funziona come un fascino!

    $(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');
      }
  });

Ciao @FarukT la tua soluzione funziona bene per me, grazie mille. Voglio chiedermi se ho due tag html supponiamo input & span e voglio calcolare offsetWidth di entrambi questi tag allo stesso modo per fare alcuni calcoli aritmetici usando quei offsetWidth. Come potrei farlo?
Kunal Tyagi

Ciao @KunalTyagi, puoi farlo con 2 funzioni separate, ne ho scritto solo uno con input e span ma puoi replicare questa funzione 2 volte, ad esempio la prima funzione con solo input e nella seconda funzione solo span. in questo modo: $ (document) .on ('mouseover', 'input', function () {...}); e il secondo $ (document) .on ('mouseover', 'span', function () {...});
FarukT

0

Nessuna delle soluzioni sopra ha funzionato per me, ma ho trovato un'ottima soluzione. L'errore più grande che le persone stanno facendo è avere tutte e 3 le proprietà CSS dichiarate sull'elemento al momento del caricamento. Devi aggiungere quegli stili + tooltip in modo dinamico SE e SOLO SE l'intervallo su cui vuoi che sia un'ellissi è più largo del suo genitore.

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

È possibile circondare la campata con un'altra campata, quindi verificare semplicemente se la larghezza della campata originale / interna è maggiore di quella della campata nuovo / esterno. Nota che dico forse - è approssimativamente basato sulla mia situazione in cui avevo un spaninterno di un tdquindi non so davvero che se funzionerà con un spaninterno di un span.

Ecco però il mio codice per gli altri che potrebbero trovarsi in una posizione simile alla mia; Lo sto copiando / incollando senza modifiche anche se si trova in un contesto angolare, non penso che toglie la leggibilità e il concetto essenziale. L'ho codificato come metodo di servizio perché dovevo applicarlo in più di un posto. Il selettore che ho passato è stato un selettore di classe che corrisponderà a più istanze.

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());
            }
        }
    });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.