| @ -0,0 +1,121 @@ | |||
| ;(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 && elem.length) { | |||
| resize.call(elem[0]); | |||
| } | |||
| }); | |||
| 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 | |||
| if(opts.animate) { | |||
| box.stop().animate({height: newHeight}, {duration: opts.speed, complete: notifyGrown}); | |||
| } else { | |||
| box.innerHeight(newHeight); | |||
| notifyGrown(); | |||
| } | |||
| } 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() | |||
| //add clone class for extra css rules | |||
| .addClass(opts.cloneClass) | |||
| //make "invisible", remove height restriction potentially imposed by existing CSS | |||
| .css({position: 'absolute', zIndex:-10, height: ''}) | |||
| //populate with content for consistent measuring | |||
| .val(box.val()) | |||
| ; | |||
| 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); | |||
| if(oldHeight > newHeight) { | |||
| if(opts.animate) { | |||
| box.stop().animate({height: newHeight}, {duration: opts.speed, complete: notifyShrunk}); | |||
| } else { | |||
| box.innerHeight(newHeight); | |||
| notifyShrunk(); | |||
| } | |||
| } | |||
| } else { //just set to the minHeight | |||
| box.innerHeight(minHeight); | |||
| } | |||
| } | |||
| } | |||
| // Trigger event to indicate a textarea has grown. | |||
| function notifyGrown() { | |||
| opts.context.trigger('autogrow:grow'); | |||
| } | |||
| // Trigger event to indicate a textarea has shrunk. | |||
| function notifyShrunk() { | |||
| opts.context.trigger('autogrow:shrink'); | |||
| } | |||
| return that; | |||
| } | |||
| })(jQuery); | |||