ref: master
public/javascripts/vendor/autogrow.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
;(function($){ //pass in just the context as a $(obj) or a settings JS object $.fn.autogrow = function(opts) { var that = $(this).css({overflow: 'hidden', resize: 'none'}) //prevent scrollies , selector = that.selector , defaults = { context: $(document) //what to wire events to , animate: true //if you want the size change to animate , speed: 200 //speed of animation , fixMinHeight: true //if you don't want the box to shrink below its initial size , cloneClass: 'autogrowclone' //helper CSS class for clone if you need to add special rules , onInitialize: false //resizes the textareas when the plugin is initialized } ; opts = $.isPlainObject(opts) ? opts : {context: opts ? opts : $(document)}; opts = $.extend({}, defaults, opts); that.each(function(i, elem){ var min, clone; elem = $(elem); //if the element is "invisible", we get an incorrect height value //to get correct value, clone and append to the body. if (elem.is(':visible') || parseInt(elem.css('height'), 10) > 0) { min = parseInt(elem.css('height'), 10) || elem.innerHeight(); } else { clone = elem.clone() .addClass(opts.cloneClass) .val(elem.val()) .css({ position: 'absolute' , visibility: 'hidden' , display: 'block' }) ; $('body').append(clone); min = clone.innerHeight(); clone.remove(); } if (opts.fixMinHeight) { elem.data('autogrow-start-height', min); //set min height } elem.css('height', min); if (opts.onInitialize) { resize.call(elem); } }); opts.context .on('keyup paste', selector, resize) ; function resize (e){ var box = $(this) , oldHeight = box.innerHeight() , newHeight = this.scrollHeight , minHeight = box.data('autogrow-start-height') || 0 , clone ; if (oldHeight < newHeight) { //user is typing this.scrollTop = 0; //try to reduce the top of the content hiding for a second opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight); } else if (!e || e.which == 8 || e.which == 46 || (e.ctrlKey && e.which == 88)) { //user is deleting, backspacing, or cutting if (oldHeight > minHeight) { //shrink! //this cloning part is not particularly necessary. however, it helps with animation //since the only way to cleanly calculate where to shrink the box to is to incrementally //reduce the height of the box until the $.innerHeight() and the scrollHeight differ. //doing this on an exact clone to figure out the height first and then applying it to the //actual box makes it look cleaner to the user clone = box.clone() .addClass(opts.cloneClass) //add clone class for extra css rules .css({position: 'absolute', zIndex:-10}) //make "invisible" .val(box.val()) //populate with content for consistent measuring ; box.after(clone); //append as close to the box as possible for best CSS matching for clone do { //reduce height until they don't match newHeight = clone[0].scrollHeight - 1; clone.innerHeight(newHeight); } while (newHeight === clone[0].scrollHeight); newHeight++; //adding one back eliminates a wiggle on deletion clone.remove(); box.focus(); // Fix issue with Chrome losing focus from the textarea. //if user selects all and deletes or holds down delete til beginning //user could get here and shrink whole box newHeight < minHeight && (newHeight = minHeight); oldHeight > newHeight && opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight); } else { //just set to the minHeight box.innerHeight(minHeight); } } } return that; } })(jQuery); |