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);
var resetDescription = function(height, originalText) {
el.html(originalText);
el.animate({ "height": height }, "normal", null, function() {
el.ellipsis(true, true, moreText, lessText);
});
}
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);
t.append(" <a class='MoreLessTag' href='#' >" + lessText + "</a>");
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 + "…" + 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 + "…" + 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);
el.animate({ "height": fullHeight }, "normal", null, function() {
});
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.