100 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| */
 | |
| ;(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: false //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: true //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)
 | |
|         ;
 | |
|         // Le code ci-dessus ne semble pas fonctionner. C'est la raison pour
 | |
|         // laquelle l'événement est provoqué ici. Le problème potentiel est
 | |
|         // que l'événement remonte toute la chaine DOM jusqu'au document.
 | |
|         if (opts.onInitialize) that.trigger('keyup');
 | |
| 
 | |
|         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);
 |