« Transitioning to Java | Main | Today is my 40th Birthday. »
Monday
09Nov2009

A Custom Ellipsis Plug-in for JQuery

I had a requirement to be able to show a long description for an item in a limited space.  The descriptions were coming from a 3rd party database and could be of any length.  The design called for the description area being two lines tall.  There is a CSS attribute called text-overflow: ellipsis.  It had several problems, however.  First of all it only worked on a single line basis. Mostly it had the big issue of not working at all in Firefox as it was a non-standard CSS call.

I found a jquery plugin to duplicate the functionality of the CSS call but like the CSS call it only worked on a single line.  My design spec also called for a More/Less link to be affixed to the block of text to expand it/ contract it.  The More/Less also had to support different cultures.

//ellipsis plugin http://devongovett.wordpress.com/2009/04/06/text-overflow-ellipsis-for-firefox-via-jquery/ + comments + custom mods
(function($) {
    $.fn.ellipsis = function(lines, enableUpdating, moreText, lessText) {
        return $(this).each(function() {
            var el = $(this);
            if (el.css("overflow") == "hidden") {
 
                var originalText = el.html();
                var availWidth = el.width();
                var availHeight = el.height();
 
                var MoreLessTag;
                if (moreText) {
                    enableUpdating = true;
                    MoreLessTag = " <a class='MoreLessTag' href='#' >" + moreText + "</a>";
                }
                else MoreLessTag = "";
 
                var t = $(this.cloneNode(true))
                    .hide()
                    .css({
                        'position': 'absolute',
                        'overflow': 'visible',
                        'max-width': 'none',
                        'max-height': 'none'
                    });
                if (lines) t.css("height", "auto").width(availWidth);
                else t.css("width", "auto");
                el.after(t);
 
                var fullHeight = t.height();
 
                var avail = (lines) ? availHeight : availWidth;
                var test = (lines) ? t.height() : t.width();
                var foundMin = false, foundMax = false;
                if (test > avail) {
                    //Binary search style trimming of the temp element to find its optimal size
                    var min = 0;
                    var max = originalText.length;
                    while (min <= max) {
                        var trimLocation = (min + max) / 2;
                        var text = originalText.substr(0, trimLocation);
                        t.html(text + "&hellip;" + MoreLessTag);
 
                        test = (lines) ? t.height() : t.width();
                        if (test > avail) {
                            if (foundMax)
                                foundMin = true;
 
                            max = trimLocation - 1;
                            if (min > max) {
                                //If we would be ending decrement the min and regenerate the text so we don't end with a
                                //slightly larger text than there is space for
                                trimLocation = (max + max - 2) / 2;
                                text = originalText.substr(0, trimLocation);
                                t.html(text + "&hellip;" + MoreLessTag);
                                break;
                            }
                        }
                        else if (test < avail) {
                            min = trimLocation + 1;
                        }
                        else {
                            if (foundMin && foundMax && ((max - min) / max < .2))
                                break;
                            foundMax = true;
                            min = trimLocation + 1;
                        }
                    }
                }
 
                el.html(t.html());
                t.remove();
 
 
                if (moreText) {
                    jQuery(".MoreLessTag", this).click(function(event) {
                        event.preventDefault();
                        el.html(originalText);
                        embeddedPlayerManager.getPlayer().hidePlayer();
                        el.animate({ "height": fullHeight }, "normal", null, function() {
                            embeddedPlayerManager.manager.updatePositions();
                            embeddedPlayerManager.getPlayer().unHidePlayer();
                        });
                        el.append(" <a class='MoreLessTag' href='#' >" + lessText + "</a>");
                        jQuery(".MoreLessTag", el).click(function(event) {
                            event.preventDefault();
                            resetDescription(availHeight, originalText);
 
                        });
                    });
                }
                else {
                    var replaceTags = new RegExp(/<\/?[^>]+>/gi);
                    el.attr("alt", originalText.replace(replaceTags, ''));
                    el.attr("title", originalText.replace(replaceTags, ''));
                }
 
                if (enableUpdating == true) {
                    var oldW = el.width();
                    var oldH = el.height();
                    el.one("resize", function() {
                        if (el.width() != oldW || (lines && el.height != oldH)) {
                            el.html(originalText);
                            el.ellipsis(lines, enableUpdating, moreText, lessText);
                        }
                    });
                }
            }
 
        });
    };
})(jQuery);

The following features are added from the original:

  • More/Less link with expansion
  • multiple lines
  • title and alt text if no more/less text is provided

This hasn't been tested extensively under different conditions.

Things I would do if I had an infinite amount of time:

  • More Testing
  • Ability to override the More/Less text click event

Enjoy – I hope someone finds this useful.  This was my first foray into doing a jQuery plugin.  Even though a good chunk of the code was copied, I still learned quite a bit.

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>