HTML - como posso mostrar apenas a dica de ferramenta quando as reticências estão ativadas

205

Eu tenho um espaço com dados dinâmicos na minha página, com ellipsisestilo.

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

Gostaria de adicionar a este elemento a dica de ferramenta com o mesmo conteúdo, mas quero que apareça apenas quando o conteúdo for longo e as reticências aparecerem na tela.

Há alguma forma de fazer isso?
O navegador lança um evento quando ellipsisestá ativado?

* Navegador: Internet Explorer

Homem Aranha
fonte
1
Lembre-se de que text-overflow:ellipsis;isso não funciona no Firefox - consulte esta pergunta para saber mais: stackoverflow.com/questions/4927257/…
Spudley 29/11
2
Eu só tinha que fazer algo semelhante. Verificando se element.offsetWidth < element.scrollWidth, de acordo com esta resposta, parece funcionar até agora.
Martin Smith
detecção de reticências: stackoverflow.com/questions/7738117/…
Adrien Be
7
Nota para a posteridade: text-overflow:ellipsis;agora funciona no Firefox; foi adicionado no Firefox 7 (lançado em setembro de 2011).
sleske

Respostas:

161

Aqui está uma maneira de fazê-lo usando a configuração de reticências incorporada e adicionando o titleatributo sob demanda (com jQuery) ao comentário de Martin Smith:

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

    if(this.offsetWidth < this.scrollWidth && !$this.attr('title')){
        $this.attr('title', $this.text());
    }
});
Jason Kleban
fonte
4
Obrigado, funciona como um encanto! No entanto, se você quiser torná-lo mais eficiente, considere substituir bind () por on () da seguinte forma: $ (document) .on ('mouseenter', '.mightOverflow', function () {...});
Ziad
17
Se você deseja que a dica de ferramenta seja removida automaticamente se o texto não estiver mais excedendo, modifique para: $ (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')}});
22813 Chris Janssen
1
mouseenter é o evento ao qual ligamos ou ouvimos. Na verdade, não precisamos adicionar a dica de ferramenta aos elementos até que alguém realmente passe o mouse sobre eles. Portanto, adiamos a adição até esse ponto de entrada do mouse em qualquer um dos elementos DOM com a classe "mightoverflow". Dicas de ferramentas 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"para obter mais informações, consulte api.jquery.com/bind e api.jquery.com/on
Adrien Seja
2
Ele não funciona no IE10 - offsetWidth é idêntico ao scrollWidth, ambos fornecendo a largura truncada.
precisa saber é o seguinte
59

Aqui está uma solução CSS pura. Não há necessidade de jQuery. Ele não mostrará uma dica de ferramenta; em vez disso, apenas expandirá o conteúdo em todo o comprimento na passagem do mouse.

Funciona muito bem se você tiver um conteúdo que seja substituído. Então você não precisa executar uma função jQuery sempre.

.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
fonte
18
Não é tão bom quanto a dica de ferramenta, porque pode quebrar o layout.
Alexander
2
O problema com as dicas de ferramentas é que elas não funcionam no celular, pelo menos por enquanto. Essa resposta pode (leia-se: provavelmente irá) interromper o layout, mas pode ser adaptada para, por exemplo, ter uma cor de fundo diferente para o elemento pairado. Isso ainda não funcionaria no celular, mas é outra opção para computadores.
trysis
1
Obrigado por esta ótima resposta que cobre perfeitamente minhas necessidades. Usando algum JavaScript, poderíamos imaginar colocar o bloco absolutamente posicionado para que ele não quebre o layout.
Niavlys 02/03
2
É uma boa resposta, melhor do que inchar o javascript em um problema de css. Claramente as reticências DEVEM ter uma opção de título automatizada, a maneira como é implementada no momento está sem sentido. Pena css não permite que somente a: hover se os últimos personagens são '...'
John
32

A resposta de uosɐſ é fundamentalmente correta, mas você provavelmente não deseja fazê-lo no evento mouseenter. Isso fará com que ele faça o cálculo para determinar se é necessário, toda vez que você passa o mouse sobre o elemento. A menos que o tamanho do elemento esteja mudando, não há razão para fazer isso.

Seria melhor chamar esse código imediatamente após o elemento ser adicionado ao DOM:

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

Ou, se você não souber exatamente quando foi adicionado, chame esse código após o término do carregamento da página.

se você tiver mais de um elemento único com o qual precisa fazer isso, poderá dar a eles a mesma classe (como "couldOverflow") e usar esse código para atualizar todos:

$('.mightOverflow').each(function() {
    var $ele = $(this);
    if (this.offsetWidth < this.scrollWidth)
        $ele.attr('title', $ele.text());
});
Elezar
fonte
+1, fazê-lo no carregamento da página parece mais simples, mas também oferece mais possibilidades. ie convém estilizar esse elemento que possui uma dica de ferramenta com um sublinhado pontilhado para "sinalizar" a dica de ferramenta. como$(".mightOverflow").each(function() { if( $(this).offsetWidth < $(this).scrollWidth && !$(this).attr('title')){ $(this).attr('title', $(this).text()); $(this).css('border-bottom', '1px dotted #A8A8A8'); } });
Adrien Seja
'cada vez que você passa o mouse' está errado (agora pelo menos - não sei se a resposta foi atualizada) Ele verifica se foi calculado anteriormente '&&! $ this.attr (' title ')'
Rune Jeppesen
Não, isso apenas impede a adição do atributo title novamente. O cálculo para determinar se é necessário ainda está feito. this.offsetWidth < this.scrollWidthe possivelmente até a verificação !$this.attr('title')será realizada sempre que você passar o mouse sobre o elemento.
Elezar
9
O problema desse método é que todos os elementos são verificados de uma só vez (impacto potencial no desempenho) e calculados apenas uma vez, para que, se o usuário encolher o navegador, as coisas ficarem truncadas ou se expandirem, fazendo com que elas não sejam mais truncadas, você não poderá mais atualize a presença de uma dica de ferramenta. Anexar seu código ao redimensionamento de janelas novamente teria um problema de desempenho, pois cada item verifica seu tamanho. Usando a delegação de evento "$ (document) .on ('mouseenter', '.mightOverflow', ..." e atrasando a verificação até passar o mouse sobre o elemento, você pode atualizar rapidamente e verificar apenas 1 elemento @ de cada vez
Chris Janssen
Ele não funciona no IE10 - offsetWidth é idêntico ao scrollWidth, ambos fornecendo a largura truncada.
precisa saber é o seguinte
18

Aqui estão duas outras soluções CSS puras:

  1. Mostrar o texto truncado no local:

.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. Mostre uma "dica de ferramenta" arbitrária :

.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
fonte
Era exatamente isso que eu estava procurando!
Praneet Nadkar
Excelente solução, exatamente o que eu preciso. Obrigado !
quer
17

Aqui está o meu 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();

Editar:

Eu fiz uma essência para este plugin. https://gist.github.com/UziTech/d45102cdffb1039d4415

Tony Brix
fonte
1
Ele não funciona no IE10 - offsetWidth é idêntico ao scrollWidth, ambos fornecendo a largura truncada.
precisa saber é o seguinte
@SsjCosty Por que você está testando no IE 10? Não deve haver um usando IE 10, neste ponto, uma vez que qualquer um que pode instalar o IE 10 pode instalar o IE 11.
Tony Brix
Ah, bem, temos uma política da empresa que nossa versão mínima do IE suporta é 10. Também porque muitos de nossos clientes (alguns bancos e várias instituições) ainda usam o IE10. Ah bem.
precisa saber é o seguinte
12

Precisamos detectar se as reticências são realmente aplicadas e, em seguida, mostrar uma dica de ferramenta para revelar o texto completo. Não basta apenas comparar "this.offsetWidth < this.scrollWidth " quando o elemento quase mantém seu conteúdo, mas apenas falta um ou dois pixels a mais, especialmente para o texto de caracteres chineses / japoneses / coreanos de largura total.

Aqui está um exemplo: http://jsfiddle.net/28r5D/5/

Eu encontrei uma maneira de melhorar a detecção de reticências:

  1. Comparar "this.offsetWidth < this.scrollWidth " primeiro, continue na etapa 2 se falhar.
  2. Alterne temporariamente o estilo css para {'overflow': 'visible', 'white-space': 'normal', 'word-break': 'break-all'}.
  3. Deixe o navegador executar a retransmissão. Se ocorrer quebra de linha, o elemento aumentará sua altura, o que também significa reticências necessárias.
  4. Restaurar estilo css.

Aqui está a minha melhoria: http://jsfiddle.net/28r5D/6/

Feng Dihai
fonte
10

Criei um plugin jQuery que usa a dica de ferramenta do Bootstrap em vez da dica de ferramenta incorporada do navegador. Observe que isso não foi testado com o navegador mais antigo.

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
fonte
Simples e brilhante. Obrigado!
GumZ
3

Foi o que eu fiz. A maioria dos scripts de dicas de ferramentas exige que você execute uma função que armazena as dicas de ferramentas. Este é um exemplo de 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 você não quis verificar reticências css, poderia simplificar como:

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

Eu tenho o "quando" ao redor, para que a função "setupTooltip" não seja executada até que todos os títulos tenham sido atualizados. Substitua "setupTooltip" pela função de dica de ferramenta e * pelos elementos que deseja verificar. * passará por todos eles se você o deixar.

Se você deseja apenas atualizar os atributos de título com a dica de ferramenta de navegador, pode simplificar:

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

Ou sem verificação de reticências:

$.when($('*').filter(function() {
   return (this.offsetWidth < this.scrollWidth && !$(this).attr('title'));
}).each(function() {
   $(this).attr('title', $(this).text());
});
fanfavorite
fonte
Por que o voto negativo? Comente se você está fazendo isso para que as pessoas saibam qual é o problema. Obrigado.
Fanfavorite 3/11
2

Se você quiser fazer isso apenas usando javascript, faça o seguinte. Atribua ao span um atributo id (para que ele possa ser facilmente recuperado do DOM) e coloque todo o conteúdo em um atributo chamado 'content':

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

Em seu javascript, você poderá fazer o seguinte depois que o elemento for inserido no 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); 
}

Obviamente, se você estiver usando javascript para inserir a extensão no DOM, poderá manter o conteúdo em uma variável antes de inseri-lo. Dessa forma, você não precisa de um atributo de conteúdo no período.

Existem soluções mais elegantes que essa se você quiser usar o jQuery.

Mark Costello
fonte
Agradável. Como estou usando o JQuery em outra parte desta página - qual é a solução elegante do 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
Eu tentei sua primeira sugestão com javascript puro - não funciona. a propriedade 'innerText' salva o comprimento completo do texto, mesmo que ele não seja exibido completamente na tela, portanto sempre: elemInnerText.length == elemContent.length !!
Spiderman
O estilo do seu vão é a largura 71. Por que não verificar se a largura da string é maior que isso? Se você quiser fazer algo realmente descolado, poderá adicionar um elemento oculto sem as reticências de texto excedente: reticências definidas e comparar as larguras de ambas. Se o oculto for maior que o não oculto, adicione um atributo de título ao visível.
Mark Costello
2

Eu tenho a classe CSS, que determina onde colocar reticências. Com base nisso, eu faço o seguinte (o conjunto de elementos pode ser diferente, eu escrevo aqueles em que as reticências são usadas, é claro que poderia ser um seletor de classe separado):

$(document).on('mouseover', 'input, td, th', function() {
    if ($(this).css('text-overflow') && typeof $(this).attr('title') === 'undefined') {
        $(this).attr('title', $(this).val());
    }
});
Alexander
fonte
1
eu meu caso thisera um, anchorentão eu usei em this.text()vez deval()
Basheer AL-MOMANI 8/17/17
2

Aqui está uma solução 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>

wuarmin
fonte
1

Esta foi a minha solução, funciona como um encanto!

    $(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
fonte
Olá @FarukT, sua solução está funcionando bem para mim, muito obrigado. Quero perguntar se tenho duas tags html, suponha input & span e quero calcular a offsetWidth dessas duas tags ao mesmo tempo para fazer alguns cálculos aritméticos usando essas offsetWidth. Como eu faria isso?
Kunal Tyagi
Oi @KunalTyagi, você pode fazer isso com 2 funções separadas, eu escrevi apenas uma com entrada e extensão, mas você pode replicar essa função 2 vezes, por exemplo, primeira função com apenas entrada e na segunda função apenas extensão. assim: $ (document) .on ('mouseover', 'input', function () {...}); e o segundo $ (document) .on ('mouseover', 'span', function () {...});
FarukT
0

Nenhuma das soluções acima funcionou para mim, mas descobri uma ótima solução. O maior erro que as pessoas estão cometendo é ter todas as três propriedades CSS declaradas no elemento após o carregamento da página. É necessário adicionar esses estilos + dica de ferramenta dinamicamente SE e SOMENTE SE o intervalo em que você deseja uma elipse for maior que seu pai.

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

Você pode cercar a extensão com outra extensão e simplesmente testar se a largura da extensão original / interna é maior que a largura da extensão nova / externa. Observe que eu digo que possivelmente - ele é basicamente baseado na minha situação em que eu tinha um spaninterior de um, tdentão eu realmente não sei se ele funcionará com um spaninterior de umspan .

Mas aqui está o meu código para outros que podem se encontrar em uma posição semelhante à minha; Estou copiando / colando sem modificação, mesmo que esteja em um contexto angular, não acho que isso prejudique a legibilidade e o conceito essencial. Eu o codifiquei como um método de serviço porque precisava aplicá-lo em mais de um local. O seletor que eu estou passando é um seletor de classe que corresponderá a várias instâncias.

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
fonte