I'm fresh to JS and i was wondering how i could implement this scenario to my code.
Im creating a simple tapping game for android using html, css and js. Whenever my object gets clicked on, 10 points gets added to score. I need this score to saved locally. So when the game restarts the score is there loaded.
Here are the game code:
var score = 0;
$(document).ready(function() {
$("#menu").hide();
$("#bug").autoBounceOff(true);
$("#bug").moveTo(140);
$("#bug").speed(0.8);
//When bug is clicked on
$("#bug").click(function() {
score = score + 10;
$('#score').html('Points: ' + score + '') + 10;
});
})
function showMenu() {
$("#menu").show();
$("#bug").css("visibility", "hidden");
$("#bug").css("animation-play-state", "paused");
return;
}
function resume() {
$("#menu").hide();
$("#bug").css("visibility", "visible");
$("#bug").css("animation-play-state", "running");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="header">
<div id="score">Points:</div>
<div id="menuheader" onclick="showMenu()">M</div>
</div>
<div id="menu">
<div id="start" onclick="resume()">resume</div>
</div>
<div id="bug"></div>
<div class="bottomwall"></div>
<div class="topwall"></div>
<div class="leftwall"></div>
<div class="rightwall"></div>
JSFiddle
basic localStorage usage.
When you starting your script, you can test if localStorage.score is already set with:
var score = 0;
if(!localStorage.score){
localStorage.score = 0;
}
score = parseInt(localStorage.score);
then in your click event:
localStorage.score = score + 10
please check this link can help you :
http://www.w3schools.com/html/html5_webstorage.asp
you can make an empty array and use: array_name.push() for your values.
then you can use localStorage.setItem("score" , array_name) and you can retrieve this value again using: localStorage.getItem("score"); that return array values and you should check first: if it has values or not to avoid undefined or exceptions in your application.
if you need a demo for this just tell me now and i will upload it.
I need to have 2 of these one page but each with different percentages. When I try re-writing the JS or even use different class/ID names it still always pulls from the first SPAN.
http://jsfiddle.net/K62Ra/
<div class="container">
<div class="bw"></div>
<div class="show"></div>
<div id="bar" data-total="100">
<div class="text">Currently at <br/><span>70</span><br><i>Click To Give</div>
</div>
JS and CSS in the Fiddle.
Much Thanks.
This one will work smoothly:
http://jsfiddle.net/K62Ra/7/
$('.bar').each(function() {
var percentStart = 0;
var total = $(this).data('total');
var percent = parseInt($(this).find('span').html());
$(this).find('> div').addClass("load");
var that = this;
var timer = setInterval(function() {
$(that).siblings('.show').css('height', percentStart/total*100+'%');
$(that).css('height', percentStart/total*100+'%');
$(that).find('span').html('%'+percentStart);
if(percentStart<percent) { percentStart=percentStart+1; return; }
clearInterval(timer);
}, 35);
});
The interval has to be terminated as well, or it will run infinitely (though not doing anything).
I've changed your id="bar" into a class. Then I'm running a each loop for the .bar classes. here is the fiddle: http://jsfiddle.net/K62Ra/3/
here is the code:
$('.bar').each(function (index, element) {
percent = $(this).find('span').html();
total = $(this).attr('data-total');
percentStart = 0;
setInterval(function () {
$('.show').css('height', percentStart / total * 100 + '%');
$(this).css('height', percentStart / total * 100 + '%');
$(this).find('span').html('%' + percentStart);
if (percentStart < percent) {
percentStart = percentStart + 1;
}
}, 35);
});
$(".bar div").addClass("load");
Like some of the comments have stated, having duplicate ids is bad design and can cause some weird errors.
You can find a solution to your problem by changing a number of things. One, instead of
referring to divs in you selectors by id'#', you can infer them by class '.' like
$('.bar')
The next step would be to ensure exclusivity for each div with class 'container' by using a closure
$('.container').each(function(){
var x
var y
.
.
});
And finally, avoid 'selecting' elements in the selector directly, but use $(this) and .find() to ensure you are within the current div with class 'container'.
http://jsfiddle.net/K62Ra/5/
$('.container').each(function(){
var percent = $(this).find('div.bar div span').html();
var total = $(this).find('div.bar').attr('data-total');
var percentStart = 0;
var that = $(this);
setInterval(function() {
that.find('.show').css('height',percentStart/total*100+'%');
that.find('div.bar').css('height',percentStart/total*100+'%');
that.find('div.bar div span').html('%'+percentStart);
if(percentStart<percent) {percentStart=percentStart+1;}
},35);
$(this).find("div.bar div").addClass("load");
});
There are already several good answers here. I would recommend validating your html. Also some of your css was causing weirdness when there was scrolling involved (fixed background images weren't scrolling.)
I took a slightly different approach than everyone else. Instead of using a setInterval I went with $.animate and a step function. Like others, I chose a class to target each of the items: 'fill-me-up'.
Fiddle: http://jsfiddle.net/LFbKs/6/
NOTE: Check the fiddle since I modified the HTML (very slightly) and the css to a larger degree.
// for each item we need to "fill up"
$('.fill-me-up').each(function(){
// cache DOM references
var this$ = $(this)
, bar$ = this$.find('.bar')
, show$ = this$.find('.show')
, span$ = bar$.find('div span')
// the target percentage height for this item
, p = span$.text()
// combine '.bar' and '.show' so we can apply the animation to both
, toAnimate = $().add(bar$).add(show$);
// add class causing fade-in
bar$.find('div').addClass('is-visible');
// animate height to target height over 2 seconds and
// at each step, update the span with a truncated value
toAnimate.animate(
{ height: p+'%' },
{
duration: 2000,
step: function( currentStep ) {
span$.html('%'+currentStep.toFixed(0));
}
}
);
});
Cheers
I have a web page with three divs that are synced together.
+----------+
| TOP |
+---+----------+
| | ||
| L | CENTER ||
| |_________||
+---+----------+
<!--Rough HTML-->
<div>
<div class="topbar-wrapper">
<div class="topbar"></div>
</div>
<div class="container">
<div class="sidebar-wrapper">
<div class="sidebar"></div>
</div>
<div class="center-wrapper"> <!-- Set to the window size & overflow:scroll -->
<div class="center"></div> <!-- Has the content -->
</div>
</div>
</div>
The center div has scroll bars, both horizontal and vertical. The top and left divs do not have scroll bars. Instead, when the horizontal scroll bar is moved, the scroll event updates the top div's margin-left appropriately. Likewise when the vertical scroll bar is moved, the margin-top is updated. In both cases, they are set to a negative value.
$('.center-wrapper').scroll(function(e) {
$('.sidebar').css({marginTop: -1 * $(this).scrollTop()});
$('.topbar').css({marginLeft: -1 * $(this).scrollLeft()});
});
This works fine in Chrome and Firefox. But in Safari, there is a delay between moving the scroll bar and the negative margin being properly set.
Is there a better way to do this? Or is there some way to get rid of the lag in Safari?
EDIT:
Check out this Fiddle to see the lag in Safari: http://jsfiddle.net/qh2K3/
Let me know if this JSFiddle works better. I experienced the same "lag" on my end too (Safari 7 on OS X) and these small CSS changes significantly improved it. My best guess is that Safari is being lazy and has not turned on its high-performance motors. We can force Safari to turn it on using some simple CSS:
.topbar-wrapper, .sidebar-wrapper, .center-wrapper {
-webkit-transform: translateZ(0);
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
}
This enables hardware accelerated CSS in Safari by offloading some of the work to the GPU. It improves rendering in the browser, which may have been the issue in the delay.
I've had this issue a few times, where safari seems to be lagging when incrementally moving elements based on events like this.
Here's how I've solved the problem in the past.
In browsers that support hardware acceleration, using any "3d" CSS property will enable the hardware acc.
If 3D transforms is supported, we might as well use translate3d to move the elements, as that makes for smoother movements than shifting pixels.
This means we first have to figure out the right prefix for the CSS transform property, then check support for 3D transforms. If we have support, translate the elements, if not shift pixels.
By figuring out the prefixes and 3D support without libraries it should be just about as fast as it can get, and has no dependencies.
var parent = document.querySelector('.center-wrapper'),
sidebar = document.querySelector('.sidebar'),
topbar = document.querySelector('.topbar'),
has3D = false,
transform = null,
transforms = {
'webkitTransform' : '-webkit-transform',
'OTransform' : '-o-transform',
'msTransform' : '-ms-transform',
'MozTransform' : '-moz-transform',
'transform' : 'transform'
};
for (var k in transforms) if (k in parent.style) transform = k;
if ( transform !== null ) {
sidebar.style[transform] = 'translate3d(0,0,0)'; // start hardware acc
topbar.style[transform] = 'translate3d(0,0,0)'; // start hardware acc
// check support
var s = window.getComputedStyle(sidebar).getPropertyValue(transforms[transform]);
has3D = s !== undefined && s.length > 0 && s !== "none";
}
if (has3D) {
parent.onscroll = function (e) {
sidebar.style[transform] = 'translate3d(0px, '+ (this.scrollTop * -1) +'px, 0px)';
topbar.style[transform] = 'translate3d('+ (this.scrollLeft * -1) +'px, 0px, 0px)';
}
}else{
parent.onscroll = function (e) {
sidebar.style.marginTop = (this.scrollTop * -1) + 'px';
topbar.style.marginLeft = (this.scrollLeft * -1) + 'px';
}
}
FIDDLE
Remember to put this after the elements in the DOM, as in right before </body>, or if that's not possible, it has to be wrapped in a DOM ready handler.
As a sidenote, querySelector is not supported in IE7 and below, if that's an issue you have to polyfill it.
Caching selectors will help.
Changing :
$('.center-wrapper').scroll(function(e) {
$('.sidebar').css({marginTop: -1 * $(this).scrollTop()});
$('.topbar').css({marginLeft: -1 * $(this).scrollLeft()});
});
To :
var _scroller = $('.center-wrapper'),
_swrapper = $('.sidebar-wrapper'),
_twrapper = $('.topbar-wrapper');
_scroller.scroll(function (e) {
_swrapper.scrollTop(_scroller.scrollTop());
_twrapper.scrollLeft(_scroller.scrollLeft());
});
.scroll() will be working very hard if it has to recreate jquery objects $(this) each time.
Wrote this in a comment last night, and while not been able to test yet, actually felt today that this is should be added as directly relates to performance.
We can try it out - here
More / The Methods - making them native too
There are also another jquery method calls we can cut out too. .scrollTop() and .scrollLeft() by caching and returning the native _scrollernative = _scroller.get(0) to make use of the readily available properties scrollLeft.
Like :
var _scroller = $('.center-wrapper'),
_scrollernative = _scroller.get(0),
_swrapper = $('.sidebar-wrapper').get(0),
_twrapper = $('.topbar-wrapper').get(0);
_scroller.scroll(function (e) {
_swrapper.scrollTop = _scrollernative.scrollTop;
_twrapper.scrollLeft = _scrollernative.scrollLeft;
});
We can try this here
I appreciate that your code might be looking at more than one of these controls on the page, and that's why you have used classes and needing to find S(this) instead of using ID's.
For that I would still use the above caching methods but before that we need to create them one by one. ( caching before we initialise the scroll() function per instance )
(function() {
var _scroller = $('.center-wrapper'),
_swrapper = $('.sidebar-wrapper').get(0),
_twrapper = $('.topbar-wrapper').get(0);
function setScrollers(_thisscroller) {
_scrollernative = _thisscroller.get(0);
/* start the scroll listener per this event */
_thisscroller.scroll(function (e) {
_swrapper.scrollTop = _scrollernative.scrollTop;
_twrapper.scrollLeft = _scrollernative.scrollLeft;
});
}
_scroller.each(function() { setScrollers($(this)); });
})();
We can try this here
But As I see that .sidebar-wrapper and .topbar-wrapper are unique, mabye .center-wrapper is unique too.
So how about finally using id's for these elements to shorten everything up.
var _scroller = document.getElementById('center-wrapper'),
_swrapper = document.getElementById('sidebar-wrapper'),
_twrapper = document.getElementById('topbar-wrapper');
_scroller.onscroll = function() {
_swrapper.scrollTop = _scroller.scrollTop;
_twrapper.scrollLeft = _scroller.scrollLeft;
};
We can try this here
Instead of using marginTop & marginLeft to change margins of .sidebar & .topbar use scrollTop & scrollLeft for the overflowing divs .sidebar-wrapper & .topbar-wrapper and see if it now works in Safari:
Demo Fiddle
$('.center-wrapper').scroll(function(e) {
$('.sidebar-wrapper').scrollTop($(this).scrollTop());
$('.topbar-wrapper').scrollLeft($(this).scrollLeft());
});
Try this fiddle made to look best in safari and tested
Okay. So I've got a a little jQuery gallery scroller I wrote to work with WordPress. It works beautifully in Firefox, but it doesn't work in Chrome or Safari.
Here's the link:
http://thehousinggroup.info/our-gallery/bathroom-renovations/gallery-1/
Here's the jQuery:
var imageQuantity = $('.galleryBox img').size() //finds the number of images
var wrapWidth = imageQuantity * 610 + 'px' //sets inner wrapper to image width*no. of images
//Formating
$('.galleryBox img')
.hide()
.unwrap()
.wrapAll('<ul></ul>')
.wrapAll('<div id="innerWrap"></div>')
.wrap('<li></li>');//wraps images in ul and div, strips off the <p> that WordPress adds
$('#innerWrap').css({
'width' : wrapWidth,
'position' : 'relative'
});
$('.galleryBox').css({'overflow' : 'hidden'}); //this css will be relegated to the stylesheet eventually...
$('.galleryBox ul').css({'list-style' : 'none'});
$('.galleryBox li').css({
'float' : 'left',
'margin-right' : '10px'
});
$('.galleryBox img').show(); //shows the images once the formatting is complete
//Scroll Controls
var currentNumber = 1; //this is for the "1 of 4" counter
var fullNumber = imageQuantity;
$('#innerWrap').before('<p id="scroller"><a id="prevButton" href="">previous</a> <span id="currentNumber">' + currentNumber + '</span> of ' + fullNumber +' <a id="nextButton" href="#">next</a></p>'); //this places the next, previous, and 1 of # counter buttons
$('#nextButton').click(function(event){
event.preventDefault();
var wrapPosition = parseInt($('#innerWrap').css('right'));
var stopPoint = (fullNumber-1)*610;
if(wrapPosition < stopPoint) { //sets the scrolling to stop at last image
$('#innerWrap').animate({'right' : "+=610px"});
++currentNumber;
$('#currentNumber').empty().html(currentNumber); //sets the counter to the proper number
}
});
$('#prevButton').click(function(event){ //same as above, reversed out for the previous button
event.preventDefault();
var wrapPosition = parseInt($('#innerWrap').css('right'));
var stopPoint = (fullNumber-1)*610;
if(wrapPosition > 0) {
$('#innerWrap').animate({'right' : "-=610px"});
--currentNumber;
$('#currentNumber').empty().html(currentNumber);
}
});
I'm going to be setting the css to be in the stylesheets, but this is how it's set up for now. If you've got any further critiques, I'm open!
Thanks.
This line catches my attention:
$('#innerWrap').animate({'right' : "-=610px"});
Specially because there is no "right" property initially set up on WebKit.
Try to have the calculation done one step above:
right_pos = doTheMathHere;
$('#innerWrap').animate({'right' : rigt_pos});
This code is breaking in Chrome : var wrapPosition = parseInt($('#innerWrap').css('right'));
So it's skipping over this block:
if(wrapPosition < stopPoint) {
$('#innerWrap').animate({'right' : "+=610px"});
++currentNumber;
$('#currentNumber').empty().html(currentNumber);
}
Sorry to answer my own question
I think I figured it out. It has to do with the wrapAll() order. I intended for the <ul> to be wrapped inside the <div>, but the opposite is happening. This isn't a problem with Webkit. It's more one of those..."wait...why does this work in Firefox" sorts of issues.
I'm currently working on an internal sales application for the company I work for, and I've got a form that allows the user to change the delivery address.
Now I think it would look much nicer, if the textarea I'm using for the main address details would just take up the area of the text in it, and automatically resize if the text was changed.
Here's a screenshot of it currently.
Any ideas?
#Chris
A good point, but there are reasons I want it to resize. I want the area it takes up to be the area of the information contained in it. As you can see in the screen shot, if I have a fixed textarea, it takes up a fair wack of vertical space.
I can reduce the font, but I need address to be large and readable. Now I can reduce the size of the text area, but then I have problems with people who have an address line that takes 3 or 4 (one takes 5) lines. Needing to have the user use a scrollbar is a major no-no.
I guess I should be a bit more specific. I'm after vertical resizing, and the width doesn't matter as much. The only problem that happens with that, is the ISO number (the large "1") gets pushed under the address when the window width is too small (as you can see on the screenshot).
It's not about having a gimick; it's about having a text field the user can edit that won't take up unnecessary space, but will show all the text in it.
Though if someone comes up with another way to approach the problem I'm open to that too.
I've modified the code a little because it was acting a little odd. I changed it to activate on keyup, because it wouldn't take into consideration the character that was just typed.
resizeIt = function() {
var str = $('iso_address').value;
var cols = $('iso_address').cols;
var linecount = 0;
$A(str.split("\n")).each(function(l) {
linecount += 1 + Math.floor(l.length / cols); // Take into account long lines
})
$('iso_address').rows = linecount;
};
Facebook does it, when you write on people's walls, but only resizes vertically.
Horizontal resize strikes me as being a mess, due to word-wrap, long lines, and so on, but vertical resize seems to be pretty safe and nice.
None of the Facebook-using-newbies I know have ever mentioned anything about it or been confused. I'd use this as anecdotal evidence to say 'go ahead, implement it'.
Some JavaScript code to do it, using Prototype (because that's what I'm familiar with):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://www.google.com/jsapi"></script>
<script language="javascript">
google.load('prototype', '1.6.0.2');
</script>
</head>
<body>
<textarea id="text-area" rows="1" cols="50"></textarea>
<script type="text/javascript" language="javascript">
resizeIt = function() {
var str = $('text-area').value;
var cols = $('text-area').cols;
var linecount = 0;
$A(str.split("\n")).each( function(l) {
linecount += Math.ceil( l.length / cols ); // Take into account long lines
})
$('text-area').rows = linecount + 1;
};
// You could attach to keyUp, etc. if keydown doesn't work
Event.observe('text-area', 'keydown', resizeIt );
resizeIt(); //Initial on load
</script>
</body>
</html>
PS: Obviously this JavaScript code is very naive and not well tested, and you probably don't want to use it on textboxes with novels in them, but you get the general idea.
One refinement to some of these answers is to let CSS do more of the work.
The basic route seems to be:
Create a container element to hold the textarea and a hidden div
Using Javascript, keep the textarea’s contents synced with the div’s
Let the browser do the work of calculating the height of that div
Because the browser handles rendering / sizing the hidden div, we avoid
explicitly setting the textarea’s height.
document.addEventListener('DOMContentLoaded', () => {
textArea.addEventListener('change', autosize, false)
textArea.addEventListener('keydown', autosize, false)
textArea.addEventListener('keyup', autosize, false)
autosize()
}, false)
function autosize() {
// Copy textarea contents to div browser will calculate correct height
// of copy, which will make overall container taller, which will make
// textarea taller.
textCopy.innerHTML = textArea.value.replace(/\n/g, '<br/>')
}
html, body, textarea {
font-family: sans-serif;
font-size: 14px;
}
.textarea-container {
position: relative;
}
.textarea-container > div, .textarea-container > textarea {
word-wrap: break-word; /* make sure the div and the textarea wrap words in the same way */
box-sizing: border-box;
padding: 2px;
width: 100%;
}
.textarea-container > textarea {
overflow: hidden;
position: absolute;
height: 100%;
}
.textarea-container > div {
padding-bottom: 1.5em; /* A bit more than one additional line of text. */
visibility: hidden;
}
<div class="textarea-container">
<textarea id="textArea"></textarea>
<div id="textCopy"></div>
</div>
Here's another technique for autosizing a textarea.
Uses pixel height instead of line height: more accurate handling of line wrap if a proportional font is used.
Accepts either ID or element as input
Accepts an optional maximum height parameter - useful if you'd rather not let the text area grow beyond a certain size (keep it all on-screen, avoid breaking layout, etc.)
Tested on Firefox 3 and Internet Explorer 6
Code:
(plain vanilla JavaScript)
function FitToContent(id, maxHeight)
{
var text = id && id.style ? id : document.getElementById(id);
if (!text)
return;
/* Accounts for rows being deleted, pixel value may need adjusting */
if (text.clientHeight == text.scrollHeight) {
text.style.height = "30px";
}
var adjustedHeight = text.clientHeight;
if (!maxHeight || maxHeight > adjustedHeight)
{
adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
if (maxHeight)
adjustedHeight = Math.min(maxHeight, adjustedHeight);
if (adjustedHeight > text.clientHeight)
text.style.height = adjustedHeight + "px";
}
}
Demo:
(uses jQuery, targets on the textarea I'm typing into right now - if you have Firebug installed, paste both samples into the console and test on this page)
$("#post-text").keyup(function()
{
FitToContent(this, document.documentElement.clientHeight)
});
Probably the shortest solution:
jQuery(document).ready(function(){
jQuery("#textArea").on("keydown keyup", function(){
this.style.height = "1px";
this.style.height = (this.scrollHeight) + "px";
});
});
This way you don't need any hidden divs or anything like that.
Note: you might have to play with this.style.height = (this.scrollHeight) + "px"; depending on how you style the textarea (line-height, padding and that kind of stuff).
Here's a Prototype version of resizing a text area that is not dependent on the number of columns in the textarea. This is a superior technique because it allows you to control the text area via CSS as well as have variable width textarea. Additionally, this version displays the number of characters remaining. While not requested, it's a pretty useful feature and is easily removed if unwanted.
//inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
if (window.Widget == undefined) window.Widget = {};
Widget.Textarea = Class.create({
initialize: function(textarea, options)
{
this.textarea = $(textarea);
this.options = $H({
'min_height' : 30,
'max_length' : 400
}).update(options);
this.textarea.observe('keyup', this.refresh.bind(this));
this._shadow = new Element('div').setStyle({
lineHeight : this.textarea.getStyle('lineHeight'),
fontSize : this.textarea.getStyle('fontSize'),
fontFamily : this.textarea.getStyle('fontFamily'),
position : 'absolute',
top: '-10000px',
left: '-10000px',
width: this.textarea.getWidth() + 'px'
});
this.textarea.insert({ after: this._shadow });
this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
this.textarea.insert({after: this._remainingCharacters});
this.refresh();
},
refresh: function()
{
this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
this.textarea.setStyle({
height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
});
var remaining = this.options.get('max_length') - $F(this.textarea).length;
this._remainingCharacters.update(Math.abs(remaining) + ' characters ' + (remaining > 0 ? 'remaining' : 'over the limit'));
}
});
Create the widget by calling new Widget.Textarea('element_id'). The default options can be overridden by passing them as an object, e.g. new Widget.Textarea('element_id', { max_length: 600, min_height: 50}). If you want to create it for all textareas on the page, do something like:
Event.observe(window, 'load', function() {
$$('textarea').each(function(textarea) {
new Widget.Textarea(textarea);
});
});
Here is a solution with JQuery:
$(document).ready(function() {
var $abc = $("#abc");
$abc.css("height", $abc.attr("scrollHeight"));
})
abc is a teaxtarea.
Check the below link:
http://james.padolsey.com/javascript/jquery-plugin-autoresize/
$(document).ready(function () {
$('.ExpandableTextCSS').autoResize({
// On resize:
onResize: function () {
$(this).css({ opacity: 0.8 });
},
// After resize:
animateCallback: function () {
$(this).css({ opacity: 1 });
},
// Quite slow animation:
animateDuration: 300,
// More extra space:
extraSpace:20,
//Textarea height limit
limit:10
});
});
Just revisiting this, I've made it a little bit tidier (though someone who is full bottle on Prototype/JavaScript could suggest improvements?).
var TextAreaResize = Class.create();
TextAreaResize.prototype = {
initialize: function(element, options) {
element = $(element);
this.element = element;
this.options = Object.extend(
{},
options || {});
Event.observe(this.element, 'keyup',
this.onKeyUp.bindAsEventListener(this));
this.onKeyUp();
},
onKeyUp: function() {
// We need this variable because "this" changes in the scope of the
// function below.
var cols = this.element.cols;
var linecount = 0;
$A(this.element.value.split("\n")).each(function(l) {
// We take long lines into account via the cols divide.
linecount += 1 + Math.floor(l.length / cols);
})
this.element.rows = linecount;
}
}
Just it call with:
new TextAreaResize('textarea_id_name_here');
I've made something quite easy. First I put the TextArea into a DIV. Second, I've called on the ready function to this script.
<div id="divTable">
<textarea ID="txt" Rows="1" TextMode="MultiLine" />
</div>
$(document).ready(function () {
var heightTextArea = $('#txt').height();
var divTable = document.getElementById('divTable');
$('#txt').attr('rows', parseInt(parseInt(divTable .style.height) / parseInt(altoFila)));
});
Simple. It is the maximum height of the div once it is rendered, divided by the height of one TextArea of one row.
I needed this function for myself, but none of the ones from here worked as I needed them.
So I used Orion's code and changed it.
I added in a minimum height, so that on the destruct it does not get too small.
function resizeIt( id, maxHeight, minHeight ) {
var text = id && id.style ? id : document.getElementById(id);
var str = text.value;
var cols = text.cols;
var linecount = 0;
var arStr = str.split( "\n" );
$(arStr).each(function(s) {
linecount = linecount + 1 + Math.floor(arStr[s].length / cols); // take into account long lines
});
linecount++;
linecount = Math.max(minHeight, linecount);
linecount = Math.min(maxHeight, linecount);
text.rows = linecount;
};
Like the answer of #memical.
However I found some improvements. You can use the jQuery height() function. But be aware of padding-top and padding-bottom pixels. Otherwise your textarea will grow too fast.
$(document).ready(function() {
$textarea = $("#my-textarea");
// There is some diff between scrollheight and height:
// padding-top and padding-bottom
var diff = $textarea.prop("scrollHeight") - $textarea.height();
$textarea.live("keyup", function() {
var height = $textarea.prop("scrollHeight") - diff;
$textarea.height(height);
});
});
My solution not using jQuery (because sometimes they don't have to be the same thing) is below. Though it was only tested in Internet Explorer 7, so the community can point out all the reasons this is wrong:
textarea.onkeyup = function () { this.style.height = this.scrollHeight + 'px'; }
So far I really like how it's working, and I don't care about other browsers, so I'll probably apply it to all my textareas:
// Make all textareas auto-resize vertically
var textareas = document.getElementsByTagName('textarea');
for (i = 0; i<textareas.length; i++)
{
// Retain textarea's starting height as its minimum height
textareas[i].minHeight = textareas[i].offsetHeight;
textareas[i].onkeyup = function () {
this.style.height = Math.max(this.scrollHeight, this.minHeight) + 'px';
}
textareas[i].onkeyup(); // Trigger once to set initial height
}
Here is an extension to the Prototype widget that Jeremy posted on June 4th:
It stops the user from entering more characters if you're using limits in textareas. It checks if there are characters left. If the user copies text into the textarea, the text is cut off at the max. length:
/**
* Prototype Widget: Textarea
* Automatically resizes a textarea and displays the number of remaining chars
*
* From: http://stackoverflow.com/questions/7477/autosizing-textarea
* Inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
*/
if (window.Widget == undefined) window.Widget = {};
Widget.Textarea = Class.create({
initialize: function(textarea, options){
this.textarea = $(textarea);
this.options = $H({
'min_height' : 30,
'max_length' : 400
}).update(options);
this.textarea.observe('keyup', this.refresh.bind(this));
this._shadow = new Element('div').setStyle({
lineHeight : this.textarea.getStyle('lineHeight'),
fontSize : this.textarea.getStyle('fontSize'),
fontFamily : this.textarea.getStyle('fontFamily'),
position : 'absolute',
top: '-10000px',
left: '-10000px',
width: this.textarea.getWidth() + 'px'
});
this.textarea.insert({ after: this._shadow });
this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
this.textarea.insert({after: this._remainingCharacters});
this.refresh();
},
refresh: function(){
this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
this.textarea.setStyle({
height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
});
// Keep the text/character count inside the limits:
if($F(this.textarea).length > this.options.get('max_length')){
text = $F(this.textarea).substring(0, this.options.get('max_length'));
this.textarea.value = text;
return false;
}
var remaining = this.options.get('max_length') - $F(this.textarea).length;
this._remainingCharacters.update(Math.abs(remaining) + ' characters remaining'));
}
});
#memical had an awesome solution for setting the height of the textarea on pageload with jQuery, but for my application I wanted to be able to increase the height of the textarea as the user added more content. I built off memical's solution with the following:
$(document).ready(function() {
var $textarea = $("p.body textarea");
$textarea.css("height", ($textarea.attr("scrollHeight") + 20));
$textarea.keyup(function(){
var current_height = $textarea.css("height").replace("px", "")*1;
if (current_height + 5 <= $textarea.attr("scrollHeight")) {
$textarea.css("height", ($textarea.attr("scrollHeight") + 20));
}
});
});
It's not very smooth but it's also not a client-facing application, so smoothness doesn't really matter. (Had this been client-facing, I probably would have just used an auto-resize jQuery plugin.)
For those that are coding for IE and encounter this problem. IE has a little trick that makes it 100% CSS.
<TEXTAREA style="overflow: visible;" cols="100" ....></TEXTAREA>
You can even provide a value for rows="n" which IE will ignore, but other browsers will use. I really hate coding that implements IE hacks, but this one is very helpful. It is possible that it only works in Quirks mode.
Internet Explorer, Safari, Chrome and Opera users need to remember to explicidly set the line-height value in CSS. I do a stylesheet that sets the initial properites for all text boxes as follows.
<style>
TEXTAREA { line-height: 14px; font-size: 12px; font-family: arial }
</style>
Here is a function I just wrote in jQuery to do it - you can port it to Prototype, but they don't support the "liveness" of jQuery so elements added by Ajax requests will not respond.
This version not only expands, but it also contracts when delete or backspace is pressed.
This version relies on jQuery 1.4.2.
Enjoy ;)
http://pastebin.com/SUKeBtnx
Usage:
$("#sometextarea").textareacontrol();
or (any jQuery selector for example)
$("textarea").textareacontrol();
It was tested on Internet Explorer 7/Internet Explorer 8, Firefox 3.5, and Chrome. All works fine.
Using ASP.NET, just simply do this:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Automatic Resize TextBox</title>
<script type="text/javascript">
function setHeight(txtarea) {
txtarea.style.height = txtdesc.scrollHeight + "px";
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox ID="txtarea" runat= "server" TextMode="MultiLine" onkeyup="setHeight(this);" onkeydown="setHeight(this);" />
</form>
</body>
</html>