Strange behaviour with textarea autogrow and CSS box-sizing - javascript

I'm trying to make an textarea autogrow with Javascript. The logic is fairly simple, and here is my working code :
$("#message-box").on('keydown', function() {
var scroll_height = $("#message-box").get(0).scrollHeight;
$("#message-box").css('height', scroll_height + 'px');
});
#message-box {
border: 1px solid #cccccc;
width: 400px;
min-height: 100px;
padding: 5px;
overflow: hidden;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="message-box"></textarea>
Everything works great, but when I remove the box-sizing: border-box; property, I see strange things. With each keydown event the textarea autogrows.
What is the relation between textarea autogrowing and the box-sizing property ?
EDIT
See the demos here :
With the box-sizing property : http://52.90.45.189/aks/textarea-autogrow.html
Without the box-sizing property : http://52.90.45.189/aks/textarea-autogrow-no-border-box.html
I can understand that scrollHeight increases by 10px when box-sizing is removed. But why does the browser add an extra 10px each time when a key is pressed ?

This is happening because scrollHeight taking padding: 5px; as a content which is increasing scroll height of textarea
The Element.scrollHeight read-only property is a measurement of the
height of an element's content, including content not visible on the
screen due to overflow.
The scrollHeight value is equal to the minimum height the element
would require in order to fit all the content in the viewpoint without
using a vertical scrollbar. It includes the element's padding, but not
its border or margin.
MDN
With border-box textarea's height is 100px excluding padding so scrollheight is 100px.
With content-box textarea's height is 100px + 10px as per default behavior of content-box so scrollheight is 110px, with each keydown textarea increases it's height by 10px and updated scrollheight as well.
See snippet Below
$("#message-box").on('keydown', function() {
console.log("height of teaxtare on keydown is " + $("#message-box").height() + "px");
var scroll_height = $("#message-box").get(0).scrollHeight;
console.log("Scroll Height of textarea is " + scroll_height + "px");
$("#message-box").css('height', scroll_height + 'px');
console.log("After setting scroll_height as a height of teaxtare, teaxtare's height is " + $("#message-box").height() + "px");
});
#message-box {
border: 1px solid #cccccc;
width: 400px;
min-height: 100px;
padding: 5px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<textarea id="message-box"></textarea>
EDIT
Let say
S = 110px (scrollheight + padding:5px;)
H = Height of textarea.
Now you presses key,
Key Event 1,
S = 110px so
H = 110px,
____
Key Event 2,
S = 120 // 110px (which is increased height of textarea by this function ($("#message-box").css('height', scroll_height + 'px');)) + Padding (Which is 10px)
H = 120px,
____
Key Event 3,
S = 130 // 120px (which is increased height of textarea by this function ($("#message-box").css('height', scroll_height + 'px');)) + Padding (Which is 10px)
H = 130px,
And So On
This formation is sort of loop.

In your JQuery, you can Use:
this.style.height = "1px";
this.style.height = (this.scrollHeight) + "px";
Please try the following:
$("#message-box").on('keydown', function() {
this.style.height = "1px";
this.style.height = (this.scrollHeight) + "px";
});
#message-box {
border: 1px solid #cccccc;
width: 400px;
min-height: 100px;
padding: 5px;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="message-box"></textarea>

In first loop iteration the CSS height was 100px but scrollHeight was 110px. These two are different things. Again in the 2nd loop iteration, scrollHeight was 120px and the CSS height before the last line of the function was 110px. After the last line the CSS height changed to 120px.
this.style.height = (this.scrollHeight) + "px";
scrollHeight is real height of the element. CSS height is the real height with border-box and not with content-box.

Related

How to increase height of textarea based font size

I have a textarea,where user will input some text,and also have a input range,where user can increase the font size of text,I want that the width of textarea still same,but height increase depend font size to show all text,and also I want textarea don't have a scroll and resize
I did that using div,and I want the same result using textarea
I did that using div https://jsfiddle.net/Meline222/fgpedL0z/1/
I want the same result but using textarea https://jsfiddle.net/Meline222/fgpedL0z/3/ bt when i use textarea all text can't see
I tried did but all text don't show textarea
this work for div
#inputText {
width: 150px;
height: auto;
border: 1px solid;
word-wrap: break-word;
overflow:hidden;
resize: none;
}
If you want only the height to be adjusted along font size, use em unit measure.
width: 150px;
height: 2em;
Documentation about the em unit says
em: 1em is the same as the font-size of the current element.
JSFiddle Demo: https://jsfiddle.net/craigiswayne/qkn8rdxp/20/
function adjustSize(i){
var o = document.getElementById('inputText');
o.setAttribute("style","height: auto;");
var val = i.value;
o.style.fontSize = val + 'px';
var fontSize = o.style.fontSize;
o.setAttribute("style","font-size: " + fontSize + "; height: " + o.scrollHeight + "px;");
}
#inputText {
width: 150px;
height: auto;
border: 1px solid;
word-wrap: break-word;
}
<textarea name="" id="inputText">kyb u uuhhhkjh kj</textarea>
<input id="input" type="range" min="12" max="72" oninput="adjustSize(this);">
So the solution I chose to set the height of the textarea to it's scrollHeight
scrollHeight is defined by MDN docs as
The scrollHeight value is equal to the minimum height the element would require in order to fit all the content in the viewport without using a vertical scrollbar
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight
see also this post for an explanation of scrollHeight, clientHeight and offsetHeight
What is offsetHeight, clientHeight, scrollHeight?

jQuery CSS method update on resize?

After struggling to vertically centre a div inside the body element using the "conventional" methods, I've decided to create a small jQuery function that figures out how far from the top an element needs to be to be "centred".
It works like this:
Get container height,
Get child height,
"top" = "(container.height - child.height) / 2"
Set margin top of child to the value of "top".
For example if the body had a width and height of 1000px and this body had a div.inner child that had a width and height of 400px the margin-top of div.inner would be 300px because (1000-400) / 2 = 300.
Here is a diagram to further explain what I mean:
NOTE: X represents the margin-top of the div.inner (as I didn't have enough space for "Margin Top = ").
To my amazement this actually works!!! Here is the test code:
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
body {
width: 100px;
height: 100px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 40px;
height: 40px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
NOTE: I made the example above smaller so you could see it properly.
Unfortunately though, there is now another problem, when I resize the browser the margin-top of the div.inner element stays the same.
I would like for it to be responsive and update it's margin-top property to the appropriate value when the window has been resized otherwise div.inner will go out of view and the page will look a like this:
You could use https://api.jquery.com/resize/
Create a function of your code
function init_center() {..
Try calling the init_center function from the resize event of window
SNIPPET
function init_center() {
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
$( window ).resize(init_center); // Handle resize of window
init_center(); // Doing it first time
body {
width: 400px;
height: 400px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 200px;
height: 200px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
Wrap you code in a function
function align() {
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
And run it on window resize as well
align(); // first run
$(window).on('resize', align); // when window resize

Need clientHeight without padding

I need to size a vertical range control based on the available height in the browser. I just about have it, except I think there is some type of a padding/border/margin issue that I can't get around. Although it sizes itself according to the height of the browser, my range control always goes off the bottom of the page by a few pixels.
This is how I'm getting the height:
var height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
then using it to set the slider height:
document.getElementById("slider").setAttribute('style', "height :" + height + "px");
I know that clientHeight returns the height INCLUDING padding, so I've been thinking that I just need to get the top padding and subtract it.
Problem is that when I get the padding as shown here, it is zero (alert writes out 0px):
alert("top pad: " + window.getComputedStyle(document.documentElement, null).getPropertyValue('padding-top'));
Meanwhile, the CSS for the slider looks like this, so I don't think its own padding/border/margins are responsible:
.sliderStyle {
height: 860px;
width: 2%;
-webkit-appearance: slider-vertical;
padding-top: 0px;
margin: 0px;
border: 0px;
}
The only thing in the body of the HTML is the range control:
<input id="slider" class="sliderStyle" oninput='' onchange='' type="range" min="0" max="1000" step="1" value="0">
Here is the file in its entirety:
<!DOCTYPE html>
<html>
<head>
<title>Size Control to Browser</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css" media="screen">
.sliderStyle {
height: 860px;
width: 2%;
-webkit-appearance: slider-vertical;
padding-top: 0px;
margin: 0px;
border: 0px;
}
</style>
<script type="text/javascript">
function start() {
var height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
alert('clientHeight: ' + height);
alert("top pad: " + window.getComputedStyle(document.documentElement, null).getPropertyValue('padding-top'));
alert("top margin: " + window.getComputedStyle(document.documentElement, null).getPropertyValue('margin-top'));
document.getElementById("slider").setAttribute('style', "height :" + height + "px");
}
</script>
</head>
<body onload='start()'>
<input id="slider" class="sliderStyle" type="range" min="0" max="1000" step="1" value="0">
</body>
</html>
clientHeight alert confirms that height is changing for different browser sizes
top pad and top margin alerts both return 0px
To answer your question: use the below JavaScript as your height variable, instead of using clientHeight or innerHeight (and set the body to be 100vh). (See the jsfiddle):
JS:
var style = window.getComputedStyle(document.body, null),
height = style.getPropertyValue("height");
console.log(height);
CSS:
body {
padding: 20px;
height: 100vh;
}
div {
height: 30px;
}
HTML:
<div></div>
What the code does is gets the height of contents of the body, which in essence is the height of the body without any padding.
The only reason I put a random div element in there with a set height of 30px is to show that console.log(height) will output exactly 30px, which is the height of all the contents in the body (in this case, the div is the only thing in the body), not including the padding of the body (which, according to the example CSS I used above, is 20px). You can obviously just remove this div, as it is just here for example purposes.
The JavaScript is taken from this answer to "Get the height of an element minus padding, margin, border widths", which also shows a legacy cross-browser implementation.

Calculate height with JQuery for sticky header

I want to change the first header to be 100% page height and then using the javascript use this height to have the sticky header appear after the first header.
So I need to calculate the height of the page I think using jquery. Not sure how to implement it.
http://jsfiddle.net/obmerk99/VvKq3/1/
#header{
width: 100%; background-color: red;
border: 1px solid black;height:40px;}
#header_stick{
width: 100%; background-color: black;
border: 1px dotted grey;color:white;}
.stick{
position:fixed;top:0;opacity:0.7;}
h1{
font-size: 130%; padding-bottom:1px;}
jQuery(window).scroll(function(){
var top = jQuery(window).scrollTop();
if(top>42) // height of float header
jQuery('#header_stick').addClass('stick');
else
jQuery('#header_stick').removeClass('stick');
})
<div id="header">My floating header</div>
<div id="header_stick">My stick header</div>
I was able to adapt your code into the following: Here's a fiddle
$(function() {
var wH = $(window).height(),
top;
$("#header").css("height", wH);
$(window).scroll(function(){
top = jQuery(window).scrollTop();
if(top>wH) // height of float header
$('#header_stick').addClass('stick');
else
$('#header_stick').removeClass('stick');
});
});
and for shiggles, watch me play this fiddle.
$(function() {
// cache vars
var wH = $(window).height(),
$stick = $("#header_stick"),
isStick;
// adjust 1st div height
$("#header").css("height", wH);
// sexier implementation with toggle
$(window).scroll(function(){
$stick.toggleClass('stick', jQuery(window).scrollTop() > wH);
});
});

Animation for the automatic height change when content is resized with javascript

I want to control the automatic height change of the container when I add something that changes the lenght of the content. Right now, if I apply a innerHTML change on the content, the height is changed accordingly. I want to apply a transition to that height change. How can I do that? ( I can also use jQuery )
Record the height before changing the content, change the content, record the height after, set the height to the former height, and animate to the latter height. When the animation has completed, set the height to be automatic again. You can do this using height and animate.
Try it on JSFiddle.
var texts = [
"This is just some sample text that's being used to demonstrate animating the height when content changes.",
"Shorter."
];
var div = $('div').click(changeContent);
function changeContent() {
var oldHeight = div.height();
texts.push(div.text());
div.text(texts.shift());
var newHeight = div.height();
div.height(oldHeight);
div.animate({height: newHeight}, 'fast', function() {
div.height('auto');
});
}
div {
width: 150px;
background: lightgray;
overflow-y: hidden;
}
<div>This is some example content.</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="containter" style="overflow:hidden">
<div>
Content.....
</div>
</div>
//add something...
$('#container').animate({height:$('#container').content().outerHeight()});
or:
$('#container').animate({height:$('#container').children().first().outerHeight()});
and when adding append to the div inside the containter:
$('#container').children().first().append(somethingNew);
Based on icktoofay's answer.
I make the button disabled while changing the height and add a fading effect. This solution is useful for updating of the products filter and so on.
Also I check the box-sizing property. If it's box-sizing then I get newHeight by .outerHeigth() instead of .height() to prevent the height fluctuation when new content has the same height. You can check this situation, for example by setting the random variable to value 5. The reason is that
.height() will always return the content height, regardless of the value of the CSS box-sizing property.
CodePen
$('#button').click(function() {
var $button = $(this),
buttonOriginalText = $button.html();
$button.prop('disabled', true).html('Updating...');
$('#content').animate({
opacity: 0
}, 'fast', function() {
var newHeight,
$content = $(this),
oldHeight = $content.height();
$content.html(getRandomContent());
newHeight = ('border-box' === $content.css('box-sizing') ? $content.outerHeight() : $content.height());
$content.height(oldHeight).animate({
height: newHeight,
opacity: 1
}, 'slow', function() {
$content.height('auto');
$button.prop('disabled', false).html(buttonOriginalText);
});
});
});
function getRandomContent() {
var random = 1 + Math.round(Math.random() * 11), // 1..12
paragraph = '<p>Paragraph</p>';
return paragraph.repeat(random);
}
* {
box-sizing: border-box; /* comment out to test "content-box" */
font: 16px Helvetica, 'sans-serif';
}
.content {
counter-reset: content;
padding: 6px 18px;
}
.content p {
counter-increment: content;
}
.content p:after {
content: ' ' counter(content) '.';
}
.content-box {
border: 2px solid red;
margin-top: 24px;
max-width: 220px;
}
<button id="button" class="button">Update the content</button>
<div class="content-box">
<div id="content" class="content">Animatie the automatic height when content is resized.</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Categories