Commit ec2272898489f98e8e7bafa76d2ea74547486300
1 parent
12208eb9
Exists in
master
and in
22 other branches
wall: correct textarea growing indefinetely
ActionItem3136
Showing
4 changed files
with
98 additions
and
11 deletions
Show diff stats
app/views/layouts/_javascript.html.erb
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', |
| 4 | 4 | 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', |
| 5 | 5 | 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', |
| 6 | -'add-and-join', 'report-abuse', 'catalog', 'manage-products', | |
| 6 | +'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow', | |
| 7 | 7 | 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %> |
| 8 | 8 | |
| 9 | 9 | <% language = FastGettext.locale %> | ... | ... |
app/views/profile/_profile_wall.html.erb
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | <div id='leave_scrap'> |
| 3 | 3 | <%= flash[:error] %> |
| 4 | 4 | <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "$('leave_scrap_content').value=''", :complete => "jQuery('#leave_scrap_form').removeClass('loading').find('*').attr('disabled', false)", :loading => "jQuery('#leave_scrap_form').addClass('loading').find('*').attr('disabled', true)", :html => {:id => 'leave_scrap_form' } do %> |
| 5 | - <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> | |
| 5 | + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2, :class => 'autogrow' %> | |
| 6 | 6 | <%= submit_button :new, _('Share') %> |
| 7 | 7 | <% end %> |
| 8 | 8 | </div> | ... | ... |
public/javascripts/application.js
| ... | ... | @@ -681,7 +681,6 @@ function hide_and_show(hide_elements, show_elements) { |
| 681 | 681 | |
| 682 | 682 | function limited_text_area(textid, limit) { |
| 683 | 683 | var text = jQuery('#' + textid).val(); |
| 684 | - grow_text_area(textid); | |
| 685 | 684 | var textlength = text.length; |
| 686 | 685 | jQuery('#' + textid + '_left span').html(limit - textlength); |
| 687 | 686 | if (textlength > limit) { |
| ... | ... | @@ -696,14 +695,9 @@ function limited_text_area(textid, limit) { |
| 696 | 695 | } |
| 697 | 696 | } |
| 698 | 697 | |
| 699 | -function grow_text_area(textid) { | |
| 700 | - var height = jQuery('#' + textid).attr('scrollHeight'); | |
| 701 | - if (jQuery.browser.webkit) { | |
| 702 | - height -= parseInt(jQuery('#' + textid).css('padding-top')) + | |
| 703 | - parseInt(jQuery('#' + textid).css('padding-bottom')); | |
| 704 | - } | |
| 705 | - jQuery('#' + textid).css('height', height + 'px'); | |
| 706 | -} | |
| 698 | +jQuery(function($) { | |
| 699 | + $('.autogrow').autogrow(); | |
| 700 | +}); | |
| 707 | 701 | |
| 708 | 702 | jQuery(function($) { |
| 709 | 703 | $('a').each(function() { | ... | ... |
| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | +;(function($){ | |
| 2 | + //pass in just the context as a $(obj) or a settings JS object | |
| 3 | + $.fn.autogrow = function(opts) { | |
| 4 | + var that = $(this).css({overflow: 'hidden', resize: 'none'}) //prevent scrollies | |
| 5 | + , selector = that.selector | |
| 6 | + , defaults = { | |
| 7 | + context: $(document) //what to wire events to | |
| 8 | + , animate: true //if you want the size change to animate | |
| 9 | + , speed: 200 //speed of animation | |
| 10 | + , fixMinHeight: true //if you don't want the box to shrink below its initial size | |
| 11 | + , cloneClass: 'autogrowclone' //helper CSS class for clone if you need to add special rules | |
| 12 | + , onInitialize: false //resizes the textareas when the plugin is initialized | |
| 13 | + } | |
| 14 | + ; | |
| 15 | + opts = $.isPlainObject(opts) ? opts : {context: opts ? opts : $(document)}; | |
| 16 | + opts = $.extend({}, defaults, opts); | |
| 17 | + that.each(function(i, elem){ | |
| 18 | + var min, clone; | |
| 19 | + elem = $(elem); | |
| 20 | + //if the element is "invisible", we get an incorrect height value | |
| 21 | + //to get correct value, clone and append to the body. | |
| 22 | + if (elem.is(':visible') || parseInt(elem.css('height'), 10) > 0) { | |
| 23 | + min = parseInt(elem.css('height'), 10) || elem.innerHeight(); | |
| 24 | + } else { | |
| 25 | + clone = elem.clone() | |
| 26 | + .addClass(opts.cloneClass) | |
| 27 | + .val(elem.val()) | |
| 28 | + .css({ | |
| 29 | + position: 'absolute' | |
| 30 | + , visibility: 'hidden' | |
| 31 | + , display: 'block' | |
| 32 | + }) | |
| 33 | + ; | |
| 34 | + $('body').append(clone); | |
| 35 | + min = clone.innerHeight(); | |
| 36 | + clone.remove(); | |
| 37 | + } | |
| 38 | + if (opts.fixMinHeight) { | |
| 39 | + elem.data('autogrow-start-height', min); //set min height | |
| 40 | + } | |
| 41 | + elem.css('height', min); | |
| 42 | + | |
| 43 | + if (opts.onInitialize) { | |
| 44 | + resize.call(elem); | |
| 45 | + } | |
| 46 | + }); | |
| 47 | + opts.context | |
| 48 | + .on('keyup paste', selector, resize) | |
| 49 | + ; | |
| 50 | + | |
| 51 | + function resize (e){ | |
| 52 | + var box = $(this) | |
| 53 | + , oldHeight = box.innerHeight() | |
| 54 | + , newHeight = this.scrollHeight | |
| 55 | + , minHeight = box.data('autogrow-start-height') || 0 | |
| 56 | + , clone | |
| 57 | + ; | |
| 58 | + if (oldHeight < newHeight) { //user is typing | |
| 59 | + this.scrollTop = 0; //try to reduce the top of the content hiding for a second | |
| 60 | + opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight); | |
| 61 | + } else if (!e || e.which == 8 || e.which == 46 || (e.ctrlKey && e.which == 88)) { //user is deleting, backspacing, or cutting | |
| 62 | + if (oldHeight > minHeight) { //shrink! | |
| 63 | + //this cloning part is not particularly necessary. however, it helps with animation | |
| 64 | + //since the only way to cleanly calculate where to shrink the box to is to incrementally | |
| 65 | + //reduce the height of the box until the $.innerHeight() and the scrollHeight differ. | |
| 66 | + //doing this on an exact clone to figure out the height first and then applying it to the | |
| 67 | + //actual box makes it look cleaner to the user | |
| 68 | + clone = box.clone() | |
| 69 | + .addClass(opts.cloneClass) //add clone class for extra css rules | |
| 70 | + .css({position: 'absolute', zIndex:-10}) //make "invisible" | |
| 71 | + .val(box.val()) //populate with content for consistent measuring | |
| 72 | + ; | |
| 73 | + box.after(clone); //append as close to the box as possible for best CSS matching for clone | |
| 74 | + do { //reduce height until they don't match | |
| 75 | + newHeight = clone[0].scrollHeight - 1; | |
| 76 | + clone.innerHeight(newHeight); | |
| 77 | + } while (newHeight === clone[0].scrollHeight); | |
| 78 | + newHeight++; //adding one back eliminates a wiggle on deletion | |
| 79 | + clone.remove(); | |
| 80 | + box.focus(); // Fix issue with Chrome losing focus from the textarea. | |
| 81 | + | |
| 82 | + //if user selects all and deletes or holds down delete til beginning | |
| 83 | + //user could get here and shrink whole box | |
| 84 | + newHeight < minHeight && (newHeight = minHeight); | |
| 85 | + oldHeight > newHeight && opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight); | |
| 86 | + } else { //just set to the minHeight | |
| 87 | + box.innerHeight(minHeight); | |
| 88 | + } | |
| 89 | + } | |
| 90 | + } | |
| 91 | + return that; | |
| 92 | + } | |
| 93 | +})(jQuery); | ... | ... |