truncate vs dotdotdot vs autoellipsis vs trunk8 (v16)

Revision 16 of this benchmark created by MadOPCode on


Description

jquery dotdotdot 1.6.16 - https://github.com/BeSite/jQuery.dotdotdot
jquery truncate 1.4 - https://github.com/tbasse/jquery-truncate
autoellipsis 1.0.10 - http://pvdspek.github.io/jquery.autoellipsis/
truncate.js v0.1.0 - https://github.com/jeffchan/truncate.js
trunk8 v1.3.1 - https://github.com/rviscomi/trunk8

Preparation HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<div id="test1" style="width: 200px; line-height: 20px;">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
<div id="test2" style="width: 200px; height: 200px; line-height: 20px;">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
<div id="test3" style="width: 200px; line-height: 20px;">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>

Setup

// jQuery truncate
    (function ($) {
      'use strict';
    
      function findTruncPoint(dim, max, txt, start, end, $worker, token, reverse) {
        var makeContent = function (content) {
          $worker.text(content);
          $worker[reverse ? 'prepend' : 'append'](token);
        };
    
        var opt1, opt2, mid, opt1dim, opt2dim;
    
        if (reverse) {
          opt1 = start === 0 ? '' : txt.slice(-start);
          opt2 = txt.slice(-end);
        } else {
          opt1 = txt.slice(0, start);
          opt2 = txt.slice(0, end);
        }
    
        if (max < $worker.html(token)[dim]()) {
          return 0;
        }
    
        makeContent(opt2);
        opt1dim = $worker[dim]();
        makeContent(opt1);
        opt2dim = $worker[dim]();
        if (opt1dim < opt2dim) {
          return end;
        }
    
        mid = parseInt((start + end) / 2, 10);
        opt1 = reverse ? txt.slice(-mid) : txt.slice(0, mid);
    
        makeContent(opt1);
        if ($worker[dim]() === max) {
          return mid;
        }
    
        if ($worker[dim]() > max) {
          end = mid - 1;
        } else {
          start = mid + 1;
        }
    
        return findTruncPoint(dim, max, txt, start, end, $worker, token, reverse);
      }
    
      $.fn.truncate = function (options) {
        // backward compatibility
        if (options && !!options.center && !options.side) {
          options.side = 'center';
          delete options.center;
        }
    
        if (options && !(/^(left|right|center)$/).test(options.side)) {
          delete options.side;
        }
    
        var defaults = {
          width: 'auto',
          token: '&hellip;',
          side: 'right',
          addclass: false,
          addtitle: false,
          multiline: false,
          assumeSameStyle: false
        };
        options = $.extend(defaults, options);
    
        var fontCSS;
        var $element;
        var $truncateWorker;
        var elementText;
        
        if (options.assumeSameStyle) {
          $element = $(this[0]);
          fontCSS = {
            'fontFamily': $element.css('fontFamily'),
            'fontSize': $element.css('fontSize'),
            'fontStyle': $element.css('fontStyle'),
            'fontWeight': $element.css('fontWeight'),
            'font-variant': $element.css('font-variant'),
            'text-indent': $element.css('text-indent'),
            'text-transform': $element.css('text-transform'),
            'letter-spacing': $element.css('letter-spacing'),
            'word-spacing': $element.css('word-spacing'),
            'display': 'none'
          };
          $truncateWorker = $('<span/>')
                             .css(fontCSS)
                             .appendTo('body');
        }
    
        return this.each(function () {
          $element = $(this);
          elementText = $element.text();
          if (!options.assumeSameStyle) {
            fontCSS = {
              'fontFamily': $element.css('fontFamily'),
              'fontSize': $element.css('fontSize'),
              'fontStyle': $element.css('fontStyle'),
              'fontWeight': $element.css('fontWeight'),
              'font-variant': $element.css('font-variant'),
              'text-indent': $element.css('text-indent'),
              'text-transform': $element.css('text-transform'),
              'letter-spacing': $element.css('letter-spacing'),
              'word-spacing': $element.css('word-spacing'),
              'display': 'none'
            };
            $truncateWorker = $('<span/>')
                               .css(fontCSS)
                               .text(elementText)
                               .appendTo('body');
          } else {
            $truncateWorker.text(elementText);
          }
          
          var originalWidth = $truncateWorker.width();
          var truncateWidth = parseInt(options.width, 10) || $element.width();
          var dimension = 'width';
          var truncatedText, originalDim, truncateDim;
    
          if (options.multiline) {
            $truncateWorker.width($element.width());
            dimension = 'height';
            originalDim = $truncateWorker.height();
            truncateDim = $element.height() + 1;
          }
          else {
            originalDim = originalWidth;
            truncateDim = truncateWidth;
          }
    
          truncatedText = {before: '', after: ''};
          if (originalDim > truncateDim) {
            var truncPoint, truncPoint2;
            $truncateWorker.text('');
    
            if (options.side === 'left') {
              truncPoint = findTruncPoint(
                dimension, truncateDim, elementText, 0, elementText.length,
                $truncateWorker, options.token, true
              );
              truncatedText.after = elementText.slice(-1 * truncPoint);
    
            } else if (options.side === 'center') {
              truncateDim = parseInt(truncateDim / 2, 10) - 1;
              truncPoint = findTruncPoint(
                dimension, truncateDim, elementText, 0, elementText.length,
                $truncateWorker, options.token, false
              );
              truncPoint2 = findTruncPoint(
                dimension, truncateDim, elementText, 0, elementText.length,
                $truncateWorker, '', true
              );
              truncatedText.before = elementText.slice(0, truncPoint);
              truncatedText.after = elementText.slice(-1 * truncPoint2);
    
            } else if (options.side === 'right') {
              truncPoint = findTruncPoint(
                dimension, truncateDim, elementText, 0, elementText.length,
                $truncateWorker, options.token, false
              );
              truncatedText.before = elementText.slice(0, truncPoint);
            }
    
            if (options.addclass) {
              $element.addClass(options.addclass);
            }
    
            if (options.addtitle) {
              $element.attr('title', elementText);
            }
    
            truncatedText.before = $truncateWorker
                                   .text(truncatedText
                                    .before).html();
            truncatedText.after = $truncateWorker
                                   .text(truncatedText.after)
                                   .html();
            $element.empty().html(
              truncatedText.before + options.token + truncatedText.after
            );
    
          }
    
          if (!options.assumeSameStyle) {
            $truncateWorker.remove();
          }
        });
        
        if (options.assumeSameStyle) {
          $truncateWorker.remove();
        }
      };
    })(jQuery);
    
    
    /**!
     * trunk8 v1.3.3
     * https://github.com/rviscomi/trunk8
     * 
     * Copyright 2012 Rick Viscomi
     * Released under the MIT License.
     * 
     * Date: September 26, 2012
     */
    
    (function ($) {
        var methods,
                utils,
                SIDES = {
                        /* cen...ter */
                        center: 'center',
                        /* ...left */
                        left: 'left',
                        /* right... */
                        right: 'right'
                },
                WIDTH = {
                        auto: 'auto'
                };
        
        function trunk8(element) {
                this.$element = $(element);
                this.original_text = $.trim(this.$element.html());
                this.settings = $.extend({}, $.fn.trunk8.defaults);
        }
        
        trunk8.prototype.updateSettings = function (options) {
                this.settings = $.extend(this.settings, options);
        };
    
        function stripHTML(html) {
                var tmp = document.createElement("DIV");
                tmp.innerHTML = html;
                
                if (typeof tmp.textContent != 'undefined') {
                        return tmp.textContent;
                }
    
                return tmp.innerText
        }
    
        function getHtmlArr(str) {
                /* Builds an array of strings and designated */
                /* HTML tags around them. */
                if (stripHTML(str) === str) {
                        return str.split(/\s/g);
                }
                var allResults = [],
                        reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>)*)<\/\1>|\s+\/>)(['.?!,]*)|((?:[^<>\s])+['.?!,]*\w?|<br\s?\/?>)/ig,
                        outArr = reg.exec(str),
                        lastI,
                        ind;
                while (outArr && lastI !== reg.lastIndex) {
                        lastI = reg.lastIndex;
                        if (outArr[5]) {
                                allResults.push(outArr[5]);
                        } else if (outArr[1]) {
                                allResults.push({
                                        tag: outArr[1],
                                        attribs: outArr[2],
                                        content: outArr[3],
                                        after: outArr[4]
                                });
                        }
                        outArr = reg.exec(str);
                }
                for (ind = 0; ind < allResults.length; ind++) {
                        if (typeof allResults[ind] !== 'string' &&
                                        allResults[ind].content) {
                                allResults[ind].content = getHtmlArr(allResults[ind].content);
                        }
                }
                return allResults;
        }
    
        function rebuildHtmlFromBite(bite, htmlObject, fill) {
                // Take the processed bite after binary-search
                // truncated and re-build the original HTML
                // tags around the processed string.
                bite = bite.replace(fill, '');
    
                var biteHelper = function(contentArr, tagInfo) {
                                var retStr = '',
                                        content,
                                        biteContent,
                                        biteLength,
                                        nextWord,
                                        i;
                                for (i = 0; i < contentArr.length; i++) {
                                        content = contentArr[i];
                                        biteLength = $.trim(bite).split(' ').length;
                                        if ($.trim(bite).length) {
                                                if (typeof content === 'string') {
                                                        if (!/<br\s*\/?>/.test(content)) {
                                                                if (biteLength === 1 && $.trim(bite).length <= content.length) {
                                                                        content = bite;
                                                                        // We want the fill to go inside of the last HTML
                                                                        // element if the element is a container.
                                                                        if (tagInfo === 'p' || tagInfo === 'div') {
                                                                                content += fill;
                                                                        }
                                                                        bite = '';
                                                                } else {
                                                                        bite = bite.replace(content, '');
                                                                }
                                                        }
                                                        retStr += $.trim(content) + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
                                                } else {
                                                        biteContent = biteHelper(content.content, content.tag);
                                                        if (content.after) bite = bite.replace(content.after, '');
                                                        if (biteContent) {
                                                                if (!content.after) content.after = ' ';
                                                                retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'>' + content.after;
                                                        }
                                                }
                                        }
                                }
                                return retStr;
                        },
                        htmlResults = biteHelper(htmlObject);
    
                // Add fill if doesn't exist. This will place it outside the HTML elements.
                if (htmlResults.slice(htmlResults.length - fill.length) === fill) {
                        htmlResults += fill;
                }
    
                return htmlResults;
        }
    
        function truncate() {
                var data = this.data('trunk8'),
                        settings = data.settings,
                        width = settings.width,
                        side = settings.side,
                        fill = settings.fill,
                        parseHTML = settings.parseHTML,
                        line_height = utils.getLineHeight(this) * settings.lines,
                        str = data.original_text,
                        length = str.length,
                        max_bite = '',
                        lower, upper,
                        bite_size,
                        bite,
                        text,
                        htmlObject;
                
                /* Reset the field to the original string. */
                this.html(str);
                text = this.text();
    
                /* If string has HTML and parse HTML is set, build */
                /* the data struct to house the tags */
                if (parseHTML && stripHTML(str) !== str) {
                        htmlObject = getHtmlArr(str);
                        str = stripHTML(str);
                        length = str.length;
                }
    
                if (width === WIDTH.auto) {
                        /* Assuming there is no "overflow: hidden". */
                        if (this.height() <= line_height) {
                                /* Text is already at the optimal trunkage. */
                                return;
                        }
    
                        /* Binary search technique for finding the optimal trunkage. */
                        /* Find the maximum bite without overflowing. */
                        lower = 0;
                        upper = length - 1;
    
                        while (lower <= upper) {
                                bite_size = lower + ((upper - lower) >> 1);
                                
                                bite = utils.eatStr(str, side, length - bite_size, fill);
    
                                if (parseHTML && htmlObject) {
                                        bite = rebuildHtmlFromBite(bite, htmlObject, fill);
                                }
                                
                                this.html(bite);
    
                                /* Check for overflow. */
                                if (this.height() > line_height) {
                                        upper = bite_size - 1;
                                }
                                else {
                                        lower = bite_size + 1;
    
                                        /* Save the bigger bite. */
                                        max_bite = (max_bite.length > bite.length) ? max_bite : bite;
                                }
                        }
    
                        /* Reset the content to eliminate possible existing scroll bars. */
                        this.html('');
                        
                        /* Display the biggest bite. */
                        this.html(max_bite);
                        
                        if (settings.tooltip) {
                                this.attr('title', text);
                        }
                }
                else if (!isNaN(width)) {
                        bite_size = length - width;
    
                        bite = utils.eatStr(str, side, bite_size, fill);
    
                        this.html(bite);
                        
                        if (settings.tooltip) {
                                this.attr('title', str);
                        }
                }
                else {
                        $.error('Invalid width "' + width + '".');
                        return;
                }
                settings.onTruncate();
        }
    
        methods = {
                init: function (options) {
                        return this.each(function () {
                                var $this = $(this),
                                        data = $this.data('trunk8');
                                
                                if (!data) {
                                        $this.data('trunk8', (data = new trunk8(this)));
                                }
                                
                                data.updateSettings(options);
                                
                                truncate.call($this);
                        });
                },
    
                /** Updates the text value of the elements while maintaining truncation. */
                update: function (new_string) {
                        return this.each(function () {
                                var $this = $(this);
                                
                                /* Update text. */
                                if (new_string) {
                                        $this.data('trunk8').original_text = new_string;
                                }
    
                                /* Truncate accordingly. */
                                truncate.call($this);
                        });
                },
                
                revert: function () {
                        return this.each(function () {
                                /* Get original text. */
                                var text = $(this).data('trunk8').original_text;
                                
                                /* Revert element to original text. */
                                $(this).html(text);
                        });
                },
    
                /** Returns this instance's settings object. NOT CHAINABLE. */
                getSettings: function () {
                        return $(this.get(0)).data('trunk8').settings;
                }
        };
    
        utils = {
                /** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
                eatStr: function (str, side, bite_size, fill) {
                        var length = str.length,
                                key = utils.eatStr.generateKey.apply(null, arguments),
                                half_length,
                                half_bite_size;
    
                        /* If the result is already in the cache, return it. */
                        if (utils.eatStr.cache[key]) {
                                return utils.eatStr.cache[key];
                        }
                        
                        /* Common error handling. */
                        if ((typeof str !== 'string') || (length === 0)) {
                                $.error('Invalid source string "' + str + '".');
                        }
                        if ((bite_size < 0) || (bite_size > length)) {
                                $.error('Invalid bite size "' + bite_size + '".');
                        }
                        else if (bite_size === 0) {
                                /* No bite should show no truncation. */
                                return str;
                        }
                        if (typeof (fill + '') !== 'string') {
                                $.error('Fill unable to be converted to a string.');
                        }
    
                        /* Compute the result, store it in the cache, and return it. */
                        switch (side) {
                                case SIDES.right:
                                        /* str... */
                                        return utils.eatStr.cache[key] =
                                                        $.trim(str.substr(0, length - bite_size)) + fill;
                                        
                                case SIDES.left:
                                        /* ...str */
                                        return utils.eatStr.cache[key] =
                                                        fill + $.trim(str.substr(bite_size));
                                        
                                case SIDES.center:
                                        /* Bit-shift to the right by one === Math.floor(x / 2) */
                                        half_length = length >> 1; // halve the length
                                        half_bite_size = bite_size >> 1; // halve the bite_size
    
                                        /* st...r */
                                        return utils.eatStr.cache[key] =
                                                        $.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
                                                        fill +
                                                        $.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));
                                        
                                default:
                                        $.error('Invalid side "' + side + '".');
                        }
                },
                
                getLineHeight: function (elem) {
                                var floats = $(elem).css('float');
                                if (floats !== 'none') {
                                        $(elem).css('float', 'none');
                                }
                                var pos = $(elem).css('position');
                                if (pos === 'absolute') {
                                        $(elem).css('position', 'static');
                                }
        
                                var html = $(elem).html(),
                                wrapper_id = 'line-height-test',
                                line_height;
        
                                /* Set the content to a small single character and wrap. */
                                $(elem).html('i').wrap('<div id="' + wrapper_id + '" />');
        
                                /* Calculate the line height by measuring the wrapper.*/
                                line_height = $('#' + wrapper_id).innerHeight();
        
                                /* Remove the wrapper and reset the content. */
                                $(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();
        
                                return line_height;
                        }
        };
    
        utils.eatStr.cache = {};
        utils.eatStr.generateKey = function () {
                return Array.prototype.join.call(arguments, '');
        };
        
        $.fn.trunk8 = function (method) {
                if (methods[method]) {
                        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
                }
                else if (typeof method === 'object' || !method) {
                        return methods.init.apply(this, arguments);
                }
                else {
                        $.error('Method ' + method + ' does not exist on jQuery.trunk8');
                }
        };
        
        /* Default trunk8 settings. */
        $.fn.trunk8.defaults = {
                fill: '&hellip;',
                lines: 1,
                side: SIDES.right,
                tooltip: true,
                width: WIDTH.auto,
                parseHTML: false,
                onTruncate: function () {}
        };
    })(jQuery);
    
    
    // truncateText v0.1
    // Copyright 2015, MadOPCode
    (function ($) {
      if ($.fn.truncateText) return;
    
      var dataAttrName = 'truncateText';
    
      function onButtonClick(event) {
        var $button = $(this);
        var $elem = $button.parent().parent();
        var elemData = $elem.data(dataAttrName);
        if (!elemData) return;
        var settings = elemData.settings;
        var $content = $elem.children('.' + settings.contentClass);
        if (elemData.state == 'truncated') {
          $content.html(elemData.originalHtml);
          $button.text(settings.buttonTextTruncate).
              removeClass(settings.buttonClassTruncated);
          elemData.state = 'restored';
          $elem.data(dataAttrName, elemData);
        } else if (elemData.state == 'restored') {
          $content.html(elemData.truncatedHtml);
          $button.text(settings.buttonTextRestore).
              addClass(settings.buttonClassTruncated);
          elemData.state = 'truncated';
          $elem.data(dataAttrName, elemData);
        }
      }
    
      function performAction(elem, action) {
        var $elem = $(elem);
        var elemData = $elem.data(dataAttrName);
        if (!elemData || elemData.state == 'intact') return;
        var settings = elemData.settings;
        var $elemChildren = $elem.children();
        var $content = $elemChildren.filter('.' + settings.contentClass);
        var $button = $elemChildren.filter('.' + settings.buttonClass);
        if (action == 'undo') {
          if (elemData.state == 'truncated') {
            $content.html(elemData.originalHtml);
            $button.text(settings.buttonTextTruncate).
                removeClass(settings.buttonClassTruncated);
            elemData.state = 'restored';
            $elem.data(dataAttrName, elemData);
          }
        } else if (action == 'redo') {
          if (elemData.state == 'restored') {
            $content.html(elemData.truncatedHtml);
            $button.text(settings.buttonTextRestore).
                addClass(settings.buttonClassTruncated);
            elemData.state = 'truncated';
            $elem.data(dataAttrName, elemData);
          }
        }
      }
    
      function traverseElem(elem, contentElem, maxHeight, ending) {
        for (var child = elem.lastChild; child; child = child.previousSibling) {
          var nodeType = child.nodeType;
          if (nodeType == 3) {
            var text = child.nodeValue;
            var words = text.split(' ');
            var lowerEnd = 0;
            var upperEnd = words.length;
            while (upperEnd - lowerEnd > 1) {
              var end = Math.round(lowerEnd + (upperEnd - lowerEnd) / 2);
              child.nodeValue = words.slice(0, end).join(' ') + ending;
              if (contentElem.offsetHeight > maxHeight) {
                upperEnd = end;
              } else {
                lowerEnd = end;
              }
            }
            if (contentElem.offsetHeight > maxHeight) {
              child.nodeValue = words.slice(0, lowerEnd).join(' ') + ending;
            } else {
              return true;
            }
            if (contentElem.offsetHeight > maxHeight) {
              child.parentNode.removeChild(child);
            } else {
              return true;
            }
          } else if (nodeType == 1) {
            if (traverseElem(child, contentElem, maxHeight, ending)) {
              return true;
            }
          }
        }
        return false;
      }
    
      function initialize(elem, settings) {
        var $elem = $(elem);
        if ($elem.data(dataAttrName)) return;
        var $elemContents = $elem.contents();
        var maxHeight = $elem.height();
        var elemData = {
          originalHtml: $elem.html(),
          settings: settings,
          state: 'truncated'
        };
        var $content = $('<div>', { 'class': settings.contentClass }).css({
          position: 'static', display: 'block', width: 'auto', height: 'auto',
          padding: 0, border: 'none', margin: 0
        }).appendTo($elem);
        $elemContents.appendTo($content);
        if ($content.height() <= maxHeight) {
          $elemContents.appendTo($elem);
          $content.remove();
          $elem.data(dataAttrName, { state: 'intact' });
          return;
        }
        var $wrapper = $('<div>').css({
          position: 'relative', display: 'block', width: 'auto', height: 'auto',
          padding: 0, border: 'none', margin: 0
        }).appendTo($elem);
        $content.appendTo($wrapper).css({
          position: 'absolute', left: 0, top: 0, right: 0
        });
        var contentElem = $content.get(0);
        traverseElem(contentElem, contentElem, maxHeight, settings.ending);
        $content.css({ position: 'static', left: '', top: '', right: '' }).
            appendTo($elem);
        $wrapper.remove();
        elemData.truncatedHtml = $content.html();
        $elem.data(dataAttrName, elemData);
        if (settings.buildFooter) {
          $elem.css({ height: 'auto', maxHeight: 'none' });
          var $footer = $('<div>', { 'class': settings.footerClass }).
              appendTo($elem);
          var $button = $('<span>', {
            'class': settings.buttonClass + ' ' + settings.buttonClassTruncated
          }).text(settings.buttonTextRestore).appendTo($footer);
          $button.on('click', onButtonClick);
        }
      }
    
      $.fn.truncateText = function (options) {
        if (typeof options === 'string') {
          if (options == 'state') {
            var elemData = this.first().data(dataAttrName);
            return elemData ? elemData.state : '';
          }
          return this.each(function () {
            performAction(this, options);
          });
        } else if (!options || typeof options === 'object') {
          return this.each(function () {
            initialize(this, $.extend({}, $.fn.truncateText.defaults, options));
          });
        }
      };
      $.fn.truncateText.defaults = {
        ending: '...',
        buildFooter: false,
        buttonTextTruncate: 'hide',
        buttonTextRestore: 'show',
        buttonClass: 'truncated-text-button',
        buttonClassTruncated: 'truncated-text-button-truncated',
        contentClass: 'truncated-text-content',
        footerClass: 'truncated-text-footer'
      };
    })(jQuery);

Test runner

Ready to run.

Testing in
TestOps/sec
truncateText
$('#test1').truncateText();
ready
truncate jquery
$('#test2').truncate();
ready
trunk8
$('#test3').trunk8();
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.