├── README.md └── jquery.expander.js /README.md: -------------------------------------------------------------------------------- 1 | jQuery Expander plugin 2 | ====================== 3 | 4 | This [jQuery](http://jquery.com/) plugin adds links to expand and shorten (truncate) plain or html texts. 5 | 6 | Installation 7 | ------------ 8 | 9 | In your HTML document: 10 | 11 | 12 | 13 | 14 | Sample use 15 | ---------- 16 | 17 | **HTML:** 18 | 19 |
Lorem ipsum dolor sit amet, consectetur adipisicing 20 | elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut 21 | enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut 22 | aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in 23 | voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 24 | occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit 25 | anim id est laborum.
26 | 27 | **Javascript:** 28 | 29 | $('p.truncate-200').expander({ 30 | slicePoint: 200, 31 | }); 32 | 33 | **Result:** 34 | 35 |Lorem ipsum dolor sit amet, consectetur adipisicing 36 | elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut 37 | enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 38 | ut… read more
39 | 40 | 41 | Available settings 42 | ------------------ 43 | 44 | * `slicePoint`: the number of characters at which the contents will be sliced 45 | into two parts. Any tag names in the HTML that appear inside the sliced 46 | element before the slicePoint will be counted along with the text characters 47 | (default: `100`). 48 | * `widow`: a threshold of sorts for whether to initially hide/collapse part of 49 | the element's contents. If after slicing the contents in two there are 50 | fewer words in the second part than the value set by widow, we won't bother 51 | hiding/collapsing anything (default: `4`). 52 | * `expandText`: text displayed in a link instead of the hidden part of the element. 53 | Clicking this will expand/show the hidden/collapsed text (default: `read more`). 54 | * `expandPrefix`: prefix to add before expanded text (default: `…`). 55 | * `collapseTimer`: number of milliseconds after text has been expanded at which 56 | to collapse the text again (default: `0`). 57 | * `expandEffect`: expanding jquery effect name (default: `fadeIn`). 58 | * `expandSpeed`: speed in milliseconds of the animation effect for expanding the text. 59 | * `userCollapse`: allow the user to re-collapse the expanded text (default: `true`). 60 | * `userCollapseText`: text to use for the link to re-collapse the text (default: `[collapse expanded text]`). 61 | * `userCollapsePrefix`: prefix to add after collapsed content (default: `' '`). 62 | * `cssReadLessClassName`: css class name to add to the read-less span (default: `re-collapse`). 63 | * `cssReadMoreClassName`: css class name to add to the read-more span (default: `read-more`). 64 | * `cssDetailsClassName`: css class name to add to the details span (default: `details`). 65 | * `truncatedSuffix`: a suffix to add to truncated inner text (default: `...`). 66 | 67 | Some callbacks are also available as settings: 68 | 69 | * `beforeExpand`: called just before the text has been expanded. 70 | * `afterExpand`: called just after the text has been expanded 71 | * `onCollapse`: called when text is collapsed. 72 | 73 | Credits 74 | ------- 75 | 76 | This plugin is a fork of the great [original Expander plugin](http://plugins.learningjquery.com/expander/), slightly adapted to my personal needs and with some added features and behaviors. 77 | -------------------------------------------------------------------------------- /jquery.expander.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery Expander plugin 3 | * Version 0.4 (12/09/2008) 4 | * @requires jQuery v1.1.1+ 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | */ 11 | (function($) { 12 | $.fn.expander = function(options) { 13 | var opts = $.extend({}, $.fn.expander.defaults, options); 14 | var delayedCollapse; 15 | return this.each(function() { 16 | var $this = $(this); 17 | var o = $.meta ? $.extend({}, 18 | opts, $this.data()) : opts; 19 | var cleanedTag, startTags, endTags; 20 | var allText = $this.html(); 21 | var startText = allText.slice(0, o.slicePoint).replace(/\w+$/, ''); 22 | startTags = startText.match(/<\w[^>]*>/g); 23 | if (startTags) { 24 | startText = allText.slice(0, o.slicePoint + startTags.join('').length).replace(/\w+$/, ''); 25 | } 26 | 27 | if (startText.lastIndexOf('<') > startText.lastIndexOf('>')) { 28 | startText = startText.slice(0, startText.lastIndexOf('<')); 29 | } 30 | var endText = allText.slice(startText.length); 31 | // create necessary expand/collapse elements if they don't already exist 32 | if (!$('span.' + o.cssDetailsClassName, this).length) { 33 | // end script if text length isn't long enough. 34 | if (endText.replace(/\s+$/, '').split(' ').length < o.widow) { 35 | return; 36 | } 37 | // otherwise, continue... 38 | if (endText.indexOf('') > -1) { 39 | endTags = endText.match(/<(\/)?[^>]*>/g); 40 | for (var i = 0; i < endTags.length; i++) { 41 | 42 | if (endTags[i].indexOf('') > -1) { 43 | var startTag, startTagExists = false; 44 | for (var j = 0; j < i; j++) { 45 | startTag = endTags[j].slice(0, endTags[j].indexOf(' ')).replace(/(\w)$/, '$1>'); 46 | if (startTag == rSlash(endTags[i])) { 47 | startTagExists = true; 48 | } 49 | } 50 | if (!startTagExists) { 51 | startText = startText + endTags[i]; 52 | var matched = false; 53 | for (var s = startTags.length - 1; s >= 0; s--) { 54 | if (startTags[s].slice(0, startTags[s].indexOf(' ')).replace(/(\w)$/, '$1>') == rSlash(endTags[i]) && matched == false) { 55 | cleanedTag = cleanedTag ? startTags[s] + cleanedTag : startTags[s]; 56 | matched = true; 57 | } 58 | }; 59 | } 60 | } 61 | } 62 | endText = cleanedTag && cleanedTag + endText || endText; 63 | } 64 | $this.html([ 65 | suffixTruncatedText(startText, o.truncatedSuffix), 66 | '', 67 | o.expandPrefix, 68 | '', 69 | ('function' == typeof(o.expandText) ? o.expandText($this) : o.expandText), 70 | '', 71 | '', 72 | '', 73 | endText, 74 | '' 75 | ].join('')); 76 | } 77 | var $thisDetails = $('span.' + o.cssDetailsClassName, this), 78 | $readMore = $('span.' + o.cssReadMoreClassName, this); 79 | $thisDetails.hide(); 80 | $readMore.find('a').click(function() { 81 | $readMore.hide(); 82 | 83 | if (o.expandEffect === 'show' && !o.expandSpeed) { 84 | o.beforeExpand($this); 85 | $thisDetails.show(); 86 | o.afterExpand($this); 87 | delayCollapse(o, $thisDetails); 88 | } else { 89 | o.beforeExpand($this); 90 | $thisDetails[o.expandEffect](o.expandSpeed, function() { 91 | $thisDetails.css({ 92 | zoom: '' 93 | }); 94 | o.afterExpand($this); 95 | delayCollapse(o, $thisDetails); 96 | }); 97 | } 98 | return false; 99 | }); 100 | if (o.userCollapse) { 101 | $this.find('span.' + o.cssDetailsClassName).append( 102 | '' + o.userCollapsePrefix + 103 | '' + 104 | ('function' == typeof(o.userCollapseText) ? o.userCollapseText($this) : o.userCollapseText) + 105 | '' + 106 | '' 107 | ); 108 | $this.find('span.' + o.cssReadLessClassName + ' a').click(function() { 109 | clearTimeout(delayedCollapse); 110 | var $detailsCollapsed = $(this).parents('span.' + o.cssDetailsClassName); 111 | $detailsCollapsed.hide().prev('span.' + o.cssReadMoreClassName).show(); 112 | o.onCollapse($this, true); 113 | return false; 114 | }); 115 | } 116 | }); 117 | 118 | function suffixTruncatedText(text, suffix) { 119 | return text.replace(/<\/(\w)>\s*$/gi, suffix + '$1>'); 120 | } 121 | 122 | function delayCollapse(option, $collapseEl) { 123 | if (option.collapseTimer) { 124 | delayedCollapse = setTimeout(function() { 125 | $collapseEl.hide().prev('span.' + o.cssReadMoreClassName).show(); 126 | option.onCollapse($collapseEl.parent(), false); 127 | }, 128 | option.collapseTimer); 129 | } 130 | } 131 | 132 | function rSlash(rString) { 133 | return rString.replace(/\//, ''); 134 | } 135 | 136 | return null; 137 | }; 138 | // plugin defaults 139 | $.fn.expander.defaults = { 140 | // the number of characters at which the contents will be sliced into two parts. 141 | // Note: any tag names in the HTML that appear inside the sliced element before 142 | // the slicePoint will be counted along with the text characters. 143 | slicePoint: 100, 144 | // a threshold of sorts for whether to initially hide/collapse part of the element's contents. 145 | // If after slicing the contents in two there are fewer words in the second part than 146 | // the value set by widow, we won't bother hiding/collapsing anything. 147 | widow: 4, 148 | // text displayed in a link instead of the hidden part of the element. 149 | // clicking this will expand/show the hidden/collapsed text 150 | expandText: 'read more', 151 | // prefix to add before expanded text 152 | expandPrefix: '… ', 153 | // number of milliseconds after text has been expanded at which to collapse the text again 154 | collapseTimer: 0, 155 | // expanding jquery effect name 156 | expandEffect: 'fadeIn', 157 | // speed in milliseconds of the animation effect for expanding the text 158 | expandSpeed: '', 159 | // allow the user to re-collapse the expanded text. 160 | userCollapse: true, 161 | // text to use for the link to re-collapse the text 162 | userCollapseText: '[collapse expanded text]', 163 | // prefix to add after collapsed content 164 | userCollapsePrefix: ' ', 165 | // css class name to add to the read-less span 166 | cssReadLessClassName: 're-collapse', 167 | // css class name to add to the read-more span 168 | cssReadMoreClassName: 'read-more', 169 | // css class name to add to the details span 170 | cssDetailsClassName: 'details', 171 | // a suffix to add to truncated inner text 172 | truncatedSuffix: '...', 173 | // some callbacks 174 | beforeExpand: function($thisEl) {}, 175 | afterExpand: function($thisEl) {}, 176 | onCollapse: function($thisEl, byUser) {} 177 | }; 178 | })(jQuery); 179 | --------------------------------------------------------------------------------