Commit ec2272898489f98e8e7bafa76d2ea74547486300
1 parent
12208eb9
Exists in
master
and in
29 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); | ... | ... |