[javascript] HTML - how can I show tooltip ONLY when ellipsis is activated

I have got a span with dynamic data in my page, with ellipsis style.

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

I'd like to add to this element tooltip with the same content, but I want it to appear only when the content is long and the ellipsis appear on screen.

Is there any way to do it?
Does the browser throw an event when ellipsis is activated?

*Browser: Internet Explorer

This question is related to javascript html css tooltip ellipsis

The answer is


Here's a Vanilla JavaScript solution:

_x000D_
_x000D_
(function init() {_x000D_
_x000D_
  var cells = document.getElementsByClassName("cell");_x000D_
_x000D_
  for(let index = 0; index < cells.length; ++index) {_x000D_
    let cell = cells.item(index);_x000D_
    cell.addEventListener('mouseenter', setTitleIfNecessary, false);_x000D_
  }_x000D_
_x000D_
  function setTitleIfNecessary() {_x000D_
    if(this.offsetWidth < this.scrollWidth) {_x000D_
      this.setAttribute('title', this.innerHTML);_x000D_
    }_x000D_
  }_x000D_
_x000D_
})();
_x000D_
.cell {_x000D_
  white-space: nowrap;_x000D_
  overflow: hidden;_x000D_
  text-overflow: ellipsis;_x000D_
  border: 1px;_x000D_
  border-style: solid;_x000D_
  width: 120px; _x000D_
}
_x000D_
<div class="cell">hello world!</div>_x000D_
<div class="cell">hello mars! kind regards, world</div>
_x000D_
_x000D_
_x000D_


Here's a way that does it using the built-in ellipsis setting, and adds the title attribute on-demand (with jQuery) building on Martin Smith's comment:

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

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

Here is my jQuery plugin:

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

Usage:

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

Edit:

I have made a gist for this plugin. https://gist.github.com/UziTech/d45102cdffb1039d4415


Here's a pure CSS solution. No need for jQuery. It won't show a tooltip, instead it'll just expand the content to its full length on mouseover.

Works great if you have content that gets replaced. Then you don't have to run a jQuery function every time.

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

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

Here are two other pure CSS solutions:

  1. Show the truncated text in place:

_x000D_
_x000D_
.overflow {_x000D_
  overflow: hidden;_x000D_
  -ms-text-overflow: ellipsis;_x000D_
  text-overflow: ellipsis;_x000D_
  white-space: nowrap;_x000D_
}_x000D_
_x000D_
.overflow:hover {_x000D_
  overflow: visible;_x000D_
}_x000D_
_x000D_
.overflow:hover span {_x000D_
  position: relative;_x000D_
  background-color: white;_x000D_
_x000D_
  box-shadow: 0 0 4px 0 black;_x000D_
  border-radius: 1px;_x000D_
}
_x000D_
<div>_x000D_
  <span class="overflow" style="float: left; width: 50px">_x000D_
    <span>Long text that might overflow.</span>_x000D_
  </span>_x000D_
  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._x000D_
</div>
_x000D_
_x000D_
_x000D_

  1. Show an arbitrary "tooltip":

_x000D_
_x000D_
.wrap {_x000D_
  position: relative;_x000D_
}_x000D_
_x000D_
.overflow {_x000D_
  white-space: nowrap; _x000D_
  overflow: hidden;_x000D_
  text-overflow: ellipsis;_x000D_
  _x000D_
  pointer-events:none;_x000D_
}_x000D_
_x000D_
.overflow:after {_x000D_
  content:"";_x000D_
  display: block;_x000D_
  position: absolute;_x000D_
  top: 0;_x000D_
  right: 0;_x000D_
  width: 20px;_x000D_
  height: 15px;_x000D_
  z-index: 1;_x000D_
  border: 1px solid red; /* for visualization only */_x000D_
  pointer-events:initial;_x000D_
_x000D_
}_x000D_
_x000D_
.overflow:hover:after{_x000D_
  cursor: pointer;_x000D_
}_x000D_
_x000D_
.tooltip {_x000D_
  /* visibility: hidden; */_x000D_
  display: none;_x000D_
  position: absolute;_x000D_
  top: 10;_x000D_
  left: 0;_x000D_
  background-color: #fff;_x000D_
  padding: 10px;_x000D_
  -webkit-box-shadow: 0 0 50px 0 rgba(0,0,0,0.3);_x000D_
  opacity: 0;_x000D_
  transition: opacity 0.5s ease;_x000D_
}_x000D_
_x000D_
_x000D_
.overflow:hover + .tooltip {_x000D_
  /*visibility: visible; */_x000D_
  display: initial;_x000D_
  transition: opacity 0.5s ease;_x000D_
  opacity: 1;_x000D_
}
_x000D_
<div>_x000D_
  <span class="wrap">_x000D_
    <span class="overflow" style="float: left; width: 50px">Long text that might overflow</span>_x000D_
    <span class='tooltip'>Long text that might overflow.</span>_x000D_
  </span>_x000D_
  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._x000D_
</div>
_x000D_
_x000D_
_x000D_


I created a jQuery plugin that uses Bootstrap's tooltip instead of the browser's build-in tooltip. Please note that this has not been tested with older browser.

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

This was my solution, works as a charm!

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

None of the solutions above worked for me, but I figured out a great solution. The biggest mistake people are making is having all the 3 CSS properties declared on the element upon pageload. You have to add those styles+tooltip dynamically IF and ONLY IF the span you want an ellipses on is wider than its parent.

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

If you want to do this solely using javascript, I would do the following. Give the span an id attribute (so that it can easily be retrieved from the DOM) and place all the content in an attribute named 'content':

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

Then, in your javascript, you can do the following after the element has been inserted into the 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); 
}

Of course, if you're using javascript to insert the span into the DOM, you could just keep the content in a variable before inserting it. This way you don't need a content attribute on the span.

There are more elegant solutions than this if you want to use jQuery.


I have CSS class, which determines where to put ellipsis. Based on that, I do the following (element set could be different, i write those, where ellipsis is used, of course it could be a separate class selector):

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

You could possibly surround the span with another span, then simply test if the width of the original/inner span is greater than that of the new/outer span. Note that I say possibly -- it is roughly based on my situation where I had a span inside of a td so I don't actually know that if it will work with a span inside of a span.

Here though is my code for others who may find themselves in a position similar to mine; I'm copying/pasting it without modification even though it is in an Angular context, I don't think that detracts from the readability and the essential concept. I coded it as a service method because I needed to apply it in more than one place. The selector I've been passing in has been a class selector that will match multiple instances.

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

uos??'s answer is fundamentally correct, but you probably don't want to do it in the mouseenter event. That's going to cause it to do the calculation to determine if it's needed, each time you mouse over the element. Unless the size of the element is changing, there's no reason to do that.

It would be better to just call this code immediately after the element is added to the DOM:

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

Or, if you don't know when exactly it's added, then call that code after the page is finished loading.

if you have more than a single element that you need to do this with, then you can give them all the same class (such as "mightOverflow"), and use this code to update them all:

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

This is what I did. Most tooltip scripts require you to execute a function that stores the tooltips. This is a jQuery example:

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

If you didn't want to check for ellipsis css, you could simplify like:

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

I have the "when" around it, so that the "setupTooltip" function doesn't execute until all titles have been updated. Replace the "setupTooltip", with your tooltip function and the * with the elements you want to check. * will go through them all if you leave it.

If you simply want to just update the title attributes with the browsers tooltip, you can simplify like:

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

Or without check for ellipsis:

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

We need to detect whether ellipsis is really applied, then to show a tooltip to reveal full text. It is not enough by only comparing "this.offsetWidth < this.scrollWidth" when the element nearly holding its content but only lacking one or two more pixels in width, especially for the text of full-width Chinese/Japanese/Korean characters.

Here is an example: http://jsfiddle.net/28r5D/5/

I found a way to improve ellipsis detection:

  1. Compare "this.offsetWidth < this.scrollWidth" first, continue step #2 if failed.
  2. Switch css style temporally to {'overflow': 'visible', 'white-space': 'normal', 'word-break': 'break-all'}.
  3. Let browser do relayout. If word-wrap happening, the element will expands its height which also means ellipsis is required.
  4. Restore css style.

Here is my improvement: http://jsfiddle.net/28r5D/6/


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width

Examples related to tooltip

Add tooltip to font awesome icon Adding a tooltip to an input box Tooltip with HTML content without JavaScript HTML-Tooltip position relative to mouse pointer Styling the arrow on bootstrap tooltips How do you change the width and height of Twitter Bootstrap's tooltips? Can I use complex HTML with Twitter Bootstrap's Tooltip? Tooltip on image Show data on mouseover of circle How to add a tooltip to an svg graphic?

Examples related to ellipsis

What is the hamburger menu icon called and the three vertical dots icon called? Applying an ellipsis to multiline text How to have Ellipsis effect on Text CSS text-overflow: ellipsis; not working? Android, How to limit width of TextView (and add three dots at the end of text)? Why doesn't CSS ellipsis work in table cell? HTML text-overflow ellipsis detection With CSS, use "..." for overflowed block of multi-lines HTML - how can I show tooltip ONLY when ellipsis is activated Limit text length to n lines using CSS