Store, change, and update opacity using JavaScript - javascript

I am new here and new to programming, so apologies in advance if this question is very basic and the answer is already here. I've done a lot of searching, but I've been unable to find the information I need.
What JavaScript code could I use to pull the current opacity value of a div, update that value, and reapply the updated value to that same div? In essence, I'm doing an exercise that involves gradually increasing the opacity of individual divs in a large grid on the mouseover event. Each individual div in the grid should have a 0.1 increase in opacity every time the mouse enters that div, up to the point of having an opacity of 1.
I already know how to do this in jQuery, I'm just trying to expand my knowledge of JavaScript at the moment.

So this is how you'd set opacities and events with jQuery:
// `elem` is the element you want to affect
// get opacity
var oldOpacity = $(elem).css('opacity');
// set opacity
$(elem).css('opacity', 0.5);
// add mouseover event
$(elem).on('mouseover', function onMouseOver(e) {
// do stuff with opacities
});
And here's how you'd do the above with vanilla DOM methods:
// `elem` is the element you want to affect
// get opacity
var oldOpacity = window.getComputedStyle(elem).getPropertyValue('opacity');
// set opacity
elem.style.setPropertyValue('opacity', 0.5);
// add mouseover event
elem.addEventListener('mouseover', function onMouseOver(e) {
// do stuff with opacities
}, false);
To get the element, you can use old DOM methods like document.getElementById or the new methods document.querySelectorAll and document.querySelector which are very much like jQuery in that they take a CSS selector and return a Node or list of Nodes.
To, say, retrieve all of the li elements with the class list-item, and iterate over them, you would do this:
var elems = document.querySelectorAll('li.list-item');
var i, l = elems.length, elem;
for (i = 0; i < l; i += 1) {
elem = elems[i];
// do stuff with elem
}

Here's the 'vanilla' JS way to check and update opacity, with the caveat that this only works in browsers; it won't work in NodeJS because there's no document in node. You can try it on this page by opening dev tools (right-click, inspect, console in Chrome).
var div = document.querySelector('.post-text')
console.log(div.style.opacity) // ""
div.style.opacity = 0.5
console.log(div.style.opacity) // "0.5"
So for your exercise, you'll want to assign the mouseover function like so:
function changeOpacity (element, delta) {
element.style.opacity = Number(element.style.opacity) + Number(delta)
}
var element = document.querySelector('.post-text')
var opacityDelta = -0.1
document.onmouseover = function () { changeOpacity(element, opacityDelta) }

I got it to work with these lines of code.
with vanilla Js.
I commented too.
// Vanilla Js.
//Getting elements.
var box = document.querySelector('.box');
var refresh = document.querySelector('.refresh');
// Assigning opacity
var defaultOpacity = 0.2;
box.style.opacity = defaultOpacity;
// Events.
// Opacity adding event on hover
box.addEventListener('mouseover', function(e){
var oldOp = e.target.style.opacity;
oldOp = Number.parseFloat(oldOp);
oldOp += defaultOpacity;
e.target.style.opacity = oldOp;
}, false);
//Refresh Evet.
refresh.addEventListener('click', function(e){
box.style.opacity = defaultOpacity;
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style>
.box {
background: #48A64C;
height: 100px;
width: 100px;
margin-bottom: 20px;
}
</head>
<body>
<div class="box"></div>
<p> Yeah you can refresh too </p>
<button class = "refresh">refresh</button>
</body>
</html>

Related

Javascript loading speed issue as variable increases

I am having an issue with page loading time. Currently right now I am running UBUNTU in Oracle Vm Virtual Box. I am using mozilla firefox as my browser and I am working on an etchasketch project from "The odin project".
My problem is the page loading time. The code takes a prompt at the start and generates a grid for the etch a sketch based on that prompt. I have not given it the minimum and maximum values (16 and 64) respectively, however any number when prompted at the beginning that is beyond 35 doesn't load or takes ages to load.
How do I speed up the process time? / why is it moving so slow? / how can I avoid this ? / is there a fix that I am over looking that can make this work a lot faster? / feel free to tackle any and all of those questions!
This is my HTML CODE:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8"/>
<title>
</title>
</head>
<body>
<div class="etchhead">
<p> Choose your grid size </p>
<input type = "text"></input>
<button id="startOver"> Clear Grid </button>
<p> Change color </p>
</div>
<div id="grid">
</div>
<script src="eas.js"></script>
</body>
</html>
And this is my CSS code:
p {
color: blue;
display: inline;
}
#grid {
display: grid;
width: 800px;
max-width: 800px;
height: 800px;
max-height: 800px;
line-height: 0;
}
.gridBox {
border: 1px solid black;
background-color: lightgrey
}
And this is my JAVASCRIPT code:
gridStart();
function gridStart(){
var boxes = 0
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
var boxCountStart = prompt("enter a number between 16 and 64");
var boxDimensions = (boxCountStart * boxCountStart);
function rowsAndColumns() {
var selectBody = document.querySelector("#grid");
var gridTemplateColumns = 'repeat('+boxCountStart+', 1fr)';
selectBody.style.gridTemplateColumns= gridTemplateColumns;
selectBody.style.gridTemplateRows= gridTemplateColumns;
};
function hoverColor(){
var divSelector = selectBody.querySelectorAll("div");
divSelector.forEach((div) => {
div.addEventListener("mouseover", (event) => {
event.target.style.backgroundColor = "grey";
});
});
};
rowsAndColumns();
for (boxes = 0; boxes < boxDimensions ; boxes++) {
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
selectBody.appendChild(addBox);
hoverColor();
};
};
There are two components to your issue. One is that you are repeatedly modifying the DOM in a loop. You can fix it by appending all your boxes to a DocumentFragment and then adding that to the DOM after your loop finishes. You are also calling hoverColor(); inside your loop which results in adding tons of event listeners that all do the same thing (since inside hoverColor you are adding a listener to every single div). You can fix both those issues like this:
var fragment = document.createDocumentFragment( );
for (var i = 0; i < boxDimensions ; i++) {
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
fragment.appendChild(addBox);
}
document.querySelector("#grid").appendChild( fragment );
hoverColor();
Here is a JSFiddle with your original code, and here is one with the modification.
You could also benefit from only having one event listener total. You don't need to loop and add an event listener to every div. Just add one to #grid and use event.target (like you already do, to find the div that the event originated from). Something like this:
function hoverColor(){
document.querySelector("#grid").addEventListener( 'mouseover', function ( event ) {
event.target.style.backgroundColor = "grey";
} );
}

An infinite carousel with vanilla JavaScript

I am trying to build my own carousel with pure JavaScript.
I'm struggling with picking up the most efficient way to add an infinite carousel option.
For some reasons, every element (photo, generic object) must have an id
The algorithm I see goes like that:
You check if the carousel is overflown (the are enough objects to fit
the whole container)
If not: append to the back a copy of the first element, then
a copy of the second element and so on. (But there will be an issue with the ids, because this object will have the same id)
- If the user is scrolling to the last object (to right) then append
the first DOM object to the array back
- If the user is scrolling to
the first object (to left) then add the last DOM child to array
front.
Is this going to work? Is there any other efficient way of doing an infinite carousel?
I have also heard that it's better to use translate property rather than changing the left, right properties, so it there would be more work for the GPU than for CPU.
I created a simple slider with css transformations as the animation technique and plain Javascript.
var img = document.getElementsByClassName("img")[0];
img.style.transform = 'translate('+value+'px)';
You can test it in this codepen snippet.
http://codepen.io/TobiObeck/pen/QKpaBr
A press on a button translates all images in the respective direction along the x-axis. An image on the edge, is set transparent outerImg.style.opacity = '0'; and translated to the other side. You can add or remove image elements in HTML and it still works.
In this second codepen snippet you can see how it works. The opacity is set to 0.5 so it is observable which image switches the side. Because overflow: hidden is removed, you can see how the images on the edge enqueue on the other side.
http://codepen.io/TobiObeck/pen/WGpdLE
Moreover it is notworthy that it is checked wether the animation is complete, otherwise the simultaneously added translations would look odd. Therefore a click won't trigger another animation until unless the animation is completed.
img.addEventListener("transitionend", transitionCompleted, true);
var transitionCompleted = function(){
translationComplete = true;
}
leftBtnCLicked(){
if(translationComplete === true){
//doAnimation
}
}
you can use this code to manipulate slides. This basically rotates the array back and front
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
width: 100%;
height: 100%;
}
.parentDiv {
height: 30%;
width: 100%;
display: flex;
}
</style>
<title>test</title>
</head>
<body>
<button class="fwd"> Fwd! </button>
<button class="bkwd"> Bkwd! </button>
<script type="text/javascript">
const arr = ['red', 'blue', 'coral', 'green', 'yellow'];
let narr = ['red', 'blue', 'coral'];
const parentDiv = document.createElement('div');
parentDiv.setAttribute('class', 'parentDiv');
document.body.insertAdjacentElement('afterbegin', parentDiv);
window.onload = ()=> {
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index]
});
};
document.querySelector('.fwd').addEventListener('click', ()=>{
narr.shift();
if(narr[narr.length-1] === arr[arr.length-1]){
narr.push(arr[0])
} else {
narr.push(arr[arr.indexOf(narr[narr.length-1])+1])
}
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index];
});
})
document.querySelector('.bkwd').addEventListener('click', ()=>{
narr.pop();
if(narr[0] === arr[0]){
narr.unshift(arr[arr.length-1])
} else {
narr.unshift(arr[arr.indexOf(narr[0])-1])
}
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index]
});
})
</script>
</body>
</html>

Most performant way to do document wide PNG animation

I've got a GIF animation that I use thoughout my site as a saving/loading icon:
Because of edges on different background colors, I'd like to change it to a PNG animation.
When I want to show a loader at this moment I only have to make sure the following span is visible:
<span class="loader"></span>
There are several ways how this span be inserted in the document: through knockout visible binding, through JS, only by stylesheets etc.
Problem
I don't want my animation code to be aware of how this span ended up visible on the document, I just want him to animate it.
Of course scanning the whole document every frame (16 fps) for potential new spans with the 'loader' class, just to know which position properties need to be animated is not quite performant.
So what would be a good performant way to do a document wide png animation?
Note that I do need to support IE8 :(
Using a sprite in combination with CSS background-position comes immediately to mind:
(yes I know it's a pretty shitty one, but it'll do the job).
You mentioned Knockout, and as its aim is to separate logic from presentation, I'll use that. However, there's no way to completely separate it that I know of that doesn't come with a performance cost (cf your comment). Typically in Knockout bindingHandlers are used to do DOM manip independently of your viewModel.
Haven't tested, but should normally work on IE8. Run the snippet below for a demo
ko.bindingHandlers.loadIndicator = {
update: function(element, valueAccessor) {
var val = ko.unwrap(valueAccessor());
if (val == true) {
var intv = setInterval(function() {
var bgX = parseInt(element.style.backgroundPosition.split(' ')[0].replace('px',''));
if (bgX > -48) // 4 frames of 14px
element.style.backgroundPosition = (bgX - 14) + 'px';
else
element.style.backgroundPosition = '0px';
}, 150);
element.style.display = 'block';
} else {
element.style.display = 'none';
clearInterval(intv);
}
}
};
var app = { loading: ko.observable(true) };
ko.applyBindings(app);
// simulate 'loaded' after 3 secs
setTimeout(function() { app.loading(false); }, 3000);
.loader {
display: block;
width: 14px;
height: 14px;
background-image: url(http://i.imgur.com/1OZACw8.png);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<span class="loader" data-bind="loadIndicator: loading"></span>
If you think a custom binding is not worth it, you can use the visible binding in combination with a transparent animated GIF (yes, it is possible). See for example: http://blog.ciuly.com/general/internet/making-animated-gif-transparent-with-gimp/.
Any way you choose, you'll have to keep track of whether something has finished loading/ saving, for example in a KO observable property.
Prefer not using Knockout? You could do the same in vanilla JS/ jQuery. However, because you have to track the status from somewhere, you have to either interval-check DOM attributes (which achieves the same as Knockout does automatically on observables) or choose a variant of the approach below where you call an init/ stop function to hide/display the loader.
function loader(container) {
var elem = document.createElement('span');
elem.className = 'loader';
container.appendChild(elem);
var intv = setInterval(function() {
var bgX = parseInt(elem.style.backgroundPosition.split(' ')[0].replace('px',''));
if (bgX > -48) // 4 frames of 14px
elem.style.backgroundPosition = (bgX - 14) + 'px';
else
elem.style.backgroundPosition = '0px';
}, 150);
this.stop = function() {
clearInterval(intv);
container.removeChild(elem);
};
}
var x = new loader(document.body);
setTimeout(function() { x.stop() }, 10000);
Performance-wise I believe the animated transparent GIF with visible binding and the vanilla init/stop method are the 2 best candidates.

How to make a text slider that moves next or previous in javascript?

Ok so I've revised the markup/code to make it easier to understand. Using JavaScript I want to know how to create a text slider that changes a paragraph in html5 either "forwards" or "backwards" on click?
I only want one div to show at a time and the first div (div_1) needs to be visible at the beginning as a default setting. I also want to be able to add more text divs to it in the future. I'm new to JavaScript so I want to keep it as simple as possible.
I've had a go creating it in JavaScript which hasn't worked, I'm not sure if I'm going about this the right way.
<!DOCTYPE html>
<html lang="en">
<head>
<style type="text/css">
.showHide {
display: none;
}
</style>
<script type="text/javascript">
var sdivs = [document.getElementById("div_1"),
document.getElementById("div_2"),
document.getElementById("div_3"),
document.getElementById("div_4")];
function openDiv(x) {
//I need to keep div_1 open as a starting point
sdivs[0].style.display ="block";
var j;
for (var j = 0; j < sdivs.length; j++) {
if (j === x) {
continue;
}
else {
sdivs[j].style.display = "none";
}
}
}
</script>
<title>text</title>
</head>
<body>
forward
backwards
<div id="text_holder">
<div id="div_1" class="showHide">One</div>
<div id="div_2" class="showHide">Two</div>
<div id="div_3" class="showHide">Three</div>
<div id="div_4" class="showHide">Four</div>
</div>
</body>
</html>
When dealing with multiple elements like this, I've found CSS alone to be insufficient (though its brilliant for modifying simple hover states or whatever). This one method here is pretty simple and specific to this one set of markup (so modify as you see fit). More importantly - its to illustrate how to set up a simple javascript "class" to handle your logic.
http://jsfiddle.net/1z13qb58/
// use a module format to keep the DOM tidy
(function($){
// define vars
var _container;
var _blurbs;
var _blurbWidth;
var _index;
var _clicks;
// initialize app
function init(){
console.log('init');
// initialize vars
_container = $('#text_holder .inner');
_blurbs = $('.blurb');
_blurbWidth = $(_blurbs[0]).innerWidth();
_clicks = $('.clicks');
_index = 0;
// assign handlers and start
styles();
addEventHandlers();
}
// initialize styles
function styles(){
_container.width(_blurbs.length * _blurbWidth);
}
// catch user interaction
function addEventHandlers(){
_clicks.on({
'click': function(el, args){
captureClicks( $(this).attr('id') );
}
});
}
// iterate _index based on click term
function captureClicks(term){
switch(term){
case 'forwards':
_index++;
if(_index > _blurbs.length - 1){
_index = 0;
}
break;
case 'backwards':
_index--;
if(_index < 0){
_index = _blurbs.length - 1;
}
break;
}
updateView();
}
// update the _container elements left value
function updateView(){
//_container.animate({
//'left' : (_index * _blurbWidth) * -1
//}, 500);
_container.css('left', ((_index * _blurbWidth) * -1) + 'px');
}
init();
})(jQuery);
I'm using jQuery to handle event binding and animation, but, again - there are lots of options (including a combination of vanilla javascript and CSS3 transitions).
I'll note also that this is all html4 and css2 (save your doctype).
Hopefully that helps -

How to autosize a textarea using Prototype?

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>

Categories