HTML: ¿cómo puedo mostrar información sobre herramientas SOLO cuando se activa la elipsis?


205

Tengo un lapso con datos dinámicos en mi página, con ellipsisestilo.

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

Me gustaría agregar a este elemento información sobre herramientas con el mismo contenido, pero quiero que aparezca solo cuando el contenido es largo y los puntos suspensivos aparecen en la pantalla.

¿Hay alguna forma de hacerlo?
¿El navegador genera un evento cuando ellipsisse activa?

* Navegador: Internet Explorer


1
Tenga en cuenta que eso text-overflow:ellipsis;no funciona en absoluto en Firefox - vea esta pregunta para más información: stackoverflow.com/questions/4927257/…
Spudley

2
Solo tenía que hacer algo similar. Comprobando si element.offsetWidth < element.scrollWidthsegún esta respuesta parece funcionar hasta ahora.
Martin Smith

detección de puntos suspensivos: stackoverflow.com/questions/7738117/…
Adrien Be

77
Nota para la posteridad: text-overflow:ellipsis;ahora funciona en Firefox; se agregó en Firefox 7 (lanzado en septiembre de 2011).
sleske

Respuestas:


161

Aquí hay una manera que lo hace usando la configuración de puntos suspensivos incorporados, y agrega el titleatributo a pedido (con jQuery) basándose en el comentario de Martin Smith:

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

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

44
Gracias, funciona como un encanto! Sin embargo, si desea hacerlo más eficiente, puede considerar reemplazar bind () con on () de esta manera: $ (document) .on ('mouseenter', '.mightOverflow', function () {...});
Ziad

17
Si desea que la información sobre herramientas se elimine automáticamente si el texto ya no se desborda, modifique a: $ (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 es el evento al que nos unimos o escuchamos. En realidad, no tenemos que agregar la información sobre herramientas a los elementos hasta que alguien realmente pase el mouse sobre ellos. Por lo tanto, diferimos la adición hasta ese punto de mouseenter en cualquiera de los elementos DOM con la clase "mightoverflow". Just-In-Time-Tooltips
Jason Kleban

2
"As of jQuery 1.7, the .on() method is the preferred method for attaching event handlers to a document"para obtener más información, visite api.jquery.com/bind y api.jquery.com/on
Adrien Be

2
No funciona en IE10: offsetWidth es idéntico a scrollWidth, ambos me dan el ancho truncado.
SsjCosty

59

Aquí hay una solución CSS pura. No hay necesidad de jQuery. No mostrará información sobre herramientas, sino que solo expandirá el contenido a su longitud completa al pasar el mouse.

Funciona muy bien si tienes contenido que se reemplaza. Entonces no tiene que ejecutar una función jQuery cada vez.

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

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

18
No es tan bueno como la información sobre herramientas, ya que puede romper el diseño.
Alexander

2
El problema con la información sobre herramientas es que no funcionan en dispositivos móviles, al menos por ahora. Esta respuesta podría (leer: probablemente lo hará) romper el diseño, pero podría adaptarse para, por ejemplo, tener un color de fondo diferente para el elemento suspendido. Esto todavía no funcionaría en dispositivos móviles, pero es otra opción para computadoras de escritorio.
trysis

1
Gracias por esta gran respuesta que cubre perfectamente mis necesidades. Usando JavaScript, podríamos imaginar que el bloque esté absolutamente posicionado para que no rompa el diseño.
Niavlys

2
Es una buena respuesta, mejor que hinchar javascript en un problema de CSS. Claramente, los puntos suspensivos DEBEN tener una opción de título automatizada, la forma en que se implementa en este momento carece de sentido. Lástima que css no permita solo: desplazarse si los últimos caracteres son '...'
John

32

La respuesta de uosɐſ es fundamentalmente correcta, pero probablemente no desee hacerlo en el evento mouseenter. Eso hará que haga el cálculo para determinar si es necesario, cada vez que pase el mouse sobre el elemento. A menos que el tamaño del elemento esté cambiando, no hay razón para hacerlo.

Sería mejor llamar a este código inmediatamente después de agregar el elemento al DOM:

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

O, si no sabe cuándo se agregó exactamente, llame a ese código una vez que la página haya terminado de cargarse.

Si tiene más de un elemento con el que necesita hacer esto, puede darles la misma clase (como "mightOverflow") y usar este código para actualizarlos a todos:

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

+1, hacerlo en la carga de la página parece más simple pero también ofrece más posibilidades. es decir. es posible que desee diseñar este elemento que tiene información sobre herramientas con un subrayado punteado para "señalar" la información sobre herramientas. 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 Be

'cada vez que pasa el mouse' es incorrecto (al menos ahora, no sé si la respuesta se actualizó) Comprueba si se calculó previamente '&&! $ this.attr (' title ')'
Rune Jeppesen

No, eso simplemente evita que vuelva a agregar el atributo de título. El cálculo para determinar si es necesario aún se realiza. this.offsetWidth < this.scrollWidthy posiblemente incluso la verificación !$this.attr('title')se realizará cada vez que pase el mouse sobre el elemento.
Elezar

9
El problema con este método es que todos los elementos se verifican a la vez (impacto potencial en el rendimiento) y solo se calcula una vez, de modo que si el usuario encoge el navegador, las cosas se truncan o se expanden, lo que hace que ya no se trunquen. actualizar la presencia de una información sobre herramientas. Adjuntar su código al cambio de tamaño de la ventana nuevamente tendría un problema de rendimiento ya que cada elemento verifica su tamaño. Al usar la delegación de eventos "$ (document) .on ('mouseenter', '.mightOverflow', ..." y retrasar la verificación hasta que pase el mouse sobre el elemento, puede actualizar sobre la marcha y solo verificar 1 elemento a la vez
Chris Janssen

No funciona en IE10: offsetWidth es idéntico a scrollWidth, ambos me dan el ancho truncado.
SsjCosty

18

Aquí hay otras dos soluciones de CSS puro:

  1. Mostrar el texto truncado en su lugar:

.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. Mostrar una "información sobre herramientas" 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>


¡Esto es exactamente lo que estaba buscando!
Praneet Nadkar

Excelente solución, exactamente lo que necesito. Gracias !
Agu V

17

Aquí está mi complemento 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:

He hecho una idea general de este complemento. https://gist.github.com/UziTech/d45102cdffb1039d4415


1
No funciona en IE10: offsetWidth es idéntico a scrollWidth, ambos me dan el ancho truncado.
SsjCosty

@SsjCosty ¿por qué estás probando en IE 10? No debería haber nadie usando IE 10 en este momento, ya que cualquiera que pueda instalar IE 10 puede instalar IE 11.
Tony Brix

Ah, bueno, tenemos una política de la compañía que admite que nuestra versión mínima de IE es 10. También porque muchos de nuestros clientes (algunos bancos y varias instituciones) todavía usan IE10. Oh bien.
SsjCosty

12

Necesitamos detectar si los puntos suspensivos se aplican realmente, y luego mostrar información sobre herramientas para revelar el texto completo. No es suficiente con solo comparar "this.offsetWidth < this.scrollWidth " cuando el elemento casi mantiene su contenido, pero solo le falta uno o dos píxeles más de ancho, especialmente para el texto de caracteres chinos / japoneses / coreanos de ancho completo.

Aquí hay un ejemplo: http://jsfiddle.net/28r5D/5/

Encontré una manera de mejorar la detección de puntos suspensivos:

  1. Comparar "this.offsetWidth < this.scrollWidth " primero, continúe con el paso 2 si falló.
  2. Cambie temporalmente el estilo CSS a {'overflow': 'visible', 'white-space': 'normal', 'word-break': 'break-all'}.
  3. Deje que el navegador realice la retransmisión. Si se produce un ajuste de texto, el elemento expandirá su altura, lo que también significa que se requiere elipsis.
  4. Restaurar estilo CSS.

Aquí está mi mejora: http://jsfiddle.net/28r5D/6/


10

Creé un complemento jQuery que utiliza la información sobre herramientas de Bootstrap en lugar de la información sobre herramientas incorporada del navegador. Tenga en cuenta que esto no se ha probado con navegadores antiguos.

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

Simple y brillante ¡Gracias!
GumZ

3

Esto es lo que hice. La mayoría de los scripts de información sobre herramientas requieren que ejecute una función que almacena la información sobre herramientas. Este es un ejemplo 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();
});

Si no desea verificar los puntos suspensivos css, puede simplificar como:

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

Tengo el "cuándo" alrededor, de modo que la función "setupTooltip" no se ejecuta hasta que se hayan actualizado todos los títulos. Reemplace el "setupTooltip", con su función de información sobre herramientas y el * con los elementos que desea verificar. * los revisará todos si lo dejas.

Si simplemente desea actualizar los atributos del título con la información sobre herramientas de los navegadores, puede simplificar como:

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

O sin verificación de puntos suspensivos:

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

¿Por qué el voto negativo? Comente si está haciendo eso para que la gente sepa cuál es el problema. Gracias.
fanfavorite

2

Si desea hacer esto únicamente con javascript, yo haría lo siguiente. Dele al palmo un atributo de identificación (para que pueda recuperarse fácilmente del DOM) y coloque todo el contenido en un atributo llamado 'contenido':

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

Luego, en su javascript, puede hacer lo siguiente después de que el elemento se haya insertado en el 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); 
}

Por supuesto, si está utilizando JavaScript para insertar el intervalo en el DOM, puede mantener el contenido en una variable antes de insertarlo. De esta manera no necesita un atributo de contenido en el lapso.

Hay soluciones más elegantes que esta si quieres usar jQuery.


Agradable. Como estoy usando JQuery en otra parte de esta página, ¿cuál es la solución elegante de 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
Intenté tu primera sugerencia con javascript puro, no funciona. la propiedad 'innerText' guarda la longitud completa del texto incluso si no se muestra completamente en la pantalla, así que siempre: elemInnerText.length == elemContent.length !!
Spiderman

El estilo de su tramo es el ancho 71. ¿Por qué no verificar si el ancho de la cadena es más largo que eso? Si desea hacer algo realmente funky, puede agregar un elemento oculto sin el desbordamiento de texto: puntos suspensivos establecidos y comparar los anchos de ambos. Si el oculto es más ancho que el no oculto, agregue un atributo de título al visible.
Mark Costello

2

Tengo la clase CSS, que determina dónde colocar puntos suspensivos. En base a eso, hago lo siguiente (el conjunto de elementos podría ser diferente, los escribo, donde se utilizan puntos suspensivos, por supuesto, podría ser un selector de clase separado):

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

1
i mi caso thisera un, anchorasí que usé en this.text()lugar deval()
Basheer AL-MOMANI

2

Aquí hay una solución JavaScript de 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

Esta fue mi solución, funciona como un 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');
      }
  });

Hola @FarukT, su solución está funcionando bien para mí, muchas gracias. Quiero preguntar si tengo dos etiquetas html, supongo input y span y quiero calcular el offsetWidth de ambas etiquetas al mismo tiempo para hacer algunos cálculos aritméticos usando esos offsetWidth. ¿Como podría hacerlo?
Kunal Tyagi

Hola @KunalTyagi, puedes hacerlo con 2 funciones separadas, solo escribí una con input y span, pero puedes replicar esta función 2 veces, por ejemplo, la primera función con solo input y en la segunda función solo span. así: $ (document) .on ('mouseover', 'input', function () {...}); y el segundo $ (document) .on ('mouseover', 'span', function () {...});
FarukT

0

Ninguna de las soluciones anteriores funcionó para mí, pero descubrí una gran solución. El mayor error que comete la gente es tener las 3 propiedades CSS declaradas en el elemento al cargar la página. Debe agregar esos estilos + información sobre herramientas dinámicamente SI y SOLO SI el intervalo en el que desea una elipsis es más ancho que su elemento primario.

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

Es posible que rodee el tramo con otro tramo, luego simplemente pruebe si el ancho del tramo original / interno es mayor que el del tramo nuevo / externo. Tenga en cuenta que posiblemente digo: se basa aproximadamente en mi situación en la que tenía un spaninterior de a, tdpor lo que no sé si funcionará con un spaninterior de a span.

Aquí, sin embargo, está mi código para otros que pueden encontrarse en una posición similar a la mía; Lo estoy copiando / pegando sin modificaciones, aunque esté en un contexto angular, no creo que eso afecte la legibilidad y el concepto esencial. Lo codifiqué como método de servicio porque necesitaba aplicarlo en más de un lugar. El selector que he estado pasando ha sido un selector de clase que coincidirá con varias instancias.

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());
            }
        }
    });
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.