Calculate the future height of a element - javascript

My intent is to animate using CSS3 the transition of height of a element when a child div get expanded.
<div id="container">
<div id="content">
<span>Small Conent</span>
<div id="big">
<p>
This is way bigger content, will be visible after you have clicked the
"Expand" button.
</p>
<p>It should animate up to the correct position.</p>
</div>
</div>
<button id="expand">Expand</button>
</div>
I came up with this hack, using max-height. But there are a couple of problems:
The max-height must have a value
The animation will start and stop according to the max-height given value, so if you insert a crazy value like 2000px the animation will have a great delay.
To better illustrate the problem, I created a Fiddle: http://jsfiddle.net/egDsE/3/
The only way of having a precise animation, is to insert the correct value of max-height.
My intention is to calculate using JavaScript (and JavaScript only) what the height of the parent will be once the child is expanded. But of course, I will need to calculate it before the actual transition takes place.
Is this possible? Only pure JS please.

Actually, you don't need to do all of the that cloning and stuff...just give the element height auto, check the size and set the height back to 0. It'll happen so fast the browser has no chance to repaint.
Now, this works like a charm, but the thing is that setting the height in Javascript immediately afterward will cause the transition to fail. I just throw a 100ms timeout around it and then it works fine.
Javascript:
document.getElementById('expand').addEventListener('click', function () {
var el = document.getElementById('big');
if (!el.className) {
el.className = 'expanded';
document.getElementById('expand').innerHTML = 'Retract';
var elH = getHeight(el);
window.setTimeout(function() {
el.style.height = elH+'px';
}, 100);
} else {
el.className = '';
el.style.height = '0';
document.getElementById('expand').innerHTML = 'Expand';
}
});
function getHeight(el) {
el.style.height = 'auto';
elHeight = el.offsetHeight;
el.style.height = '0';
return elHeight;
}
CSS:
#container {
border: 1px solid gray;
padding: 20px;
}
#big {
overflow: hidden;
height: 0;
transition: height 0.5s ease-in-out;
-webkit-transition: height 0.5s ease-in-out;
-moz-transition: height 0.5s ease-in-out;
-o-transition: height 0.5s ease-in-out;
}
HTML: no changes to your markup
<div id="container">
<div id="content"> <span>Small Conent</span>
<div id="big">
<p>This is way bigger content, will be visible after you have clicked the "Expand" button.</p>
<p>It should animate up to the correct position.</p>
</div>
</div>
<button id="expand">Expand</button>
</div>
DEMO

I have updated the Fiddle with the solution proposed in the comments by Bartdude, optimizing it for performances as possible.
http://jsfiddle.net/egDsE/5/
var calculateHeight = function(e) {
var outside = document.getElementById('outside');
var clone = e.cloneNode(true);
outside.appendChild(clone);
var elHeight = outside.offsetHeight;
outside.removeChild(clone);
return elHeight;
}
The outside div have very basic css:
#outside {
position: absolute;
top: -1000;
left: -1000;
}
I won't recommend this solution if image are involved, they would slow down the operation significantly.

not to be that guy but I would use LESS if you don't want to be as intensive and get this functionality

Related

CSS Transition on height not working when fully done from Javascript

I'm trying to make an animation for sliding up and sliding down. To toggle parts of the page. When the height is "hardcoded" in CSS to 210px before rendering the page and I call the Javascript function from a button it all works.
But when I try to do this dynamically, with Javascript to keep "hardcoding" to a minimum. It still does the change to the height. But the transition effect does not happen.
Here is the code snippet to high light the problem. I don't understand as to why this difference sabotages the transition.
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.height = "" + target.clientHeight+"px"; // taking the rendered height of the div and setting it in CSS to mimic the pre set height in CSS
target.style.transition = "height 1.0s ease-in 0s";
target.style.height = "0px";
}
.divStyle {
/* height: 200px; without this the animation does not work */
background: blue;
overflow: hidden;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
Use of max-height combined with setTimeout "hack" worked for me:
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.maxHeight = target.clientHeight + "px";
setTimeout(function() {
target.style.maxHeight = 0;
}, 10);
}
.divStyle {
background: blue;
overflow: hidden;
transition: max-height 1s ease-in 0s;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
However, it is not a clean way. Think of using transform: scaleY(0) instead.
There are multiple ways to achieve the goal, in your case you just require to init the style which is supposed to change at the later time so just use window.onload and init the value.
var org = "";
window.onload = function() {
var target = document.getElementById("targetDiv");
org = target.clientHeight;
target.style.height = "" + target.clientHeight + "px";
}
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.transition = "height 1.0s ease-in 0s";
if (target.clientHeight == org) {
target.style.height = "0px";
} else {
target.style.height = org + "px";
}
}
.divStyle {
/* height: 200px; without this the animation does not work */
background: blue;
overflow: hidden;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
Just so that if anyone comes along here, the given answers are good, just not in the current case I need. The way I managed finally is as follows.
function slideDown( targetId ){
var target = document.getElementById(targetId);
target.style.height = target.children[0].clientHeight + "px";
}
function slideUp( targetId ) {
var target = document.getElementById(targetId);
target.style.height = "0px";
}
.parentDivStyle{
overflow: hidden;
transition: height 0.5s ease-in 0s;
height: 0px;
background: blue;
}
.childDivStyle {
background: green;
padding: 10px 10px 10px 10px;
}
<div id="parent" class="parentDivStyle">
<div class="childDivStyle">
random stuff
text text text...........
</div>
</div>
<button onclick="slideUp('parent')"> Slide Up </button>
<button onclick="slideDown('parent')"> Slide Down </button>
This was actually a comment in a deleted answer. Idk why it was deleted. Was useful.
As mentioned before: having a height/max-height value set to auto/none stops
the transition from working.
Rather than setting the max-height to a specified, larger than needed
max-height by guesswork (if set to really high number causes a delay in the
transition), I added transition and a max-height to the content wrapper instead,
and used JavaScript to read the actual height of the contents. This way the
wrapper max-height is set to the currently rendered height of its contents.
This gives it a fixed inline height.
The problem with this solution, is that if the user decides to resize his
window, into say, a narrower screen (i.e. rotating from landscape to portrait
mode on mobile), the fixed max-height causes to content to be cut-off. The
work-around for this is setting a setTimeout() function to set the max-height
of the wrapper back to none just after the transition has ended. (none is
the default, initial CSS value of the max-height property)
The same thing needs to happen before the "contracting" transition. The
wrapper's max-height needs to be a fixed value for the transition to work.
Therefore, the height needs to again be read from its contents (which might have
actually changed in the meantime), applied to the wrapper, then after a short
delay (50ms seemed to work in my case, and the delay wasn't noticeable) begin
the transition.
Here's an example that expands the div contents when a button is pressed:
HTML:
<div id="content-wrapper">
<div id="content">
<!-- a lot of text content -->
</div>
</div>
<button id="show-more" type="button">Show More</button>
CSS:
#content-wrapper {
/* Initial max-height, can be any value including 0 */
max-height: 270px;
overflow: hidden;
/* transition occurs on max-height change with specified delay; */
transition: max-height 0.7s;
}
#content {
/* This makes margin of the contents be respected by the container
i.e. the height of the container its childrens' margins */
overflow: hidden;
}
JS:
function resetHeight() {
// make max-height of wrapper responsive again
wrapper.style.maxHeight = "none";
}
function toggleText() {
// check if content-wrapper is open
if (wrapper.className.includes("open")) {
// if is open, close content-wrapper and set initial max-height to wrapper
let contentHeight = window.getComputedStyle(content).height;
// sets max-height back from none to computed content height
wrapper.style.maxHeight = contentHeight;
// give it some time to reset the transition trigger caused by previous statement
setTimeout(function() {
wrapper.classList.remove("open");
wrapper.style.maxHeight = "270px"; // reset to our initial max-height
button.textContent = "Show More";
}, 50)
} else {
// if not open, open content-wrapper then use content's height to set the
// max-height of wrapper
wrapper.classList.add("open");
let contentHeight = window.getComputedStyle(content).height;
wrapper.style.maxHeight = contentHeight;
button.textContent = "Show Less";
setTimeout(resetHeight, 700) // the timeout is the same as the transition time
}
}
const content = document.getElementById("content")
const wrapper = document.getElementById("content-wrapper");
const button = document.getElementById("show-more");
button.addEventListener("click", toggleText);
This is my first answer on StackOverflow, so if there is anything I can improve
please do let me know.

How to calculate height in pixel according to content if set as 0px in css?

I gave height of 0px to an element as shown below
#sample{
height: 0px;
overflow: hidden;
transition-duration: 1.2s;
}
<div id="sample">
<h1>Hello World main headinh</h1>
<h2>Hello World sub heading</h2>
<p>Hello world paragraph</p>
</div>
If I'd not specified height so it will take height of content (say 50px). Now initially I dont know the height of #sample. I want to set its height to its original calculated height (i.e. 50px), I can do it by giving height 100%.
document.getElementById("sample").style.height = "100%";
But transition-duration property is not working in that case, so when I change percent into pixel so transition-duration property worked well.
document.getElementById("sample").style.height = "50px";
But problem here is the content inside the div is dyanamic, so its height is changing too.
So how can I give height to the #sample in pixel as much as that content need.
Solution should only have Javascript not any other framework like JQuery.
try this.
var element = document.getElementById('sample');
element.style.height= element.scrollHeight+"px"
https://jsfiddle.net/y0g22540/
If you're wanting to animate the height, what you want to do is start out by setting max-height: 0, and then transition the max-height by changing a class and adding a transition or setting an animation, such that max-height: 500px or some value that's definitely bigger than the content inside will be.
EDIT: This might not be exactly what you're doing, but here is a decent example: https://davidwalsh.name/css-slide
Percentage heights will only work when the parent element has had its height set (you can't have a percentage of something that hasn't had its height set).
However, you can set the height back to its original value using scrollHeight.
// Get references to the elements:
var btn = document.getElementById("show");
var sample = document.getElementById("sample");
// Set up an event handler to trigger the code:
btn.addEventListener("click", function(){
// Set the height to the height of the element's content:
sample.style.height = sample.scrollHeight + "px";
// Just for testing purposes:
console.log(sample.style.height);
});
#sample{
height: 0px;
overflow: hidden;
transition-duration: 1.2s;
}
<button id="show">Show</button>
<div id="sample">
<h1>Hello World main headinh</h1>
<h2>Hello World sub heading</h2>
<p>Hello world paragraph</p>
</div>
If you just want to show and hide shrinking or expanding the width and height then here it is and example that will point you in that direction!
#click:checked ~ #sample{
-webkit-transform: scale(1);
-moz-transform: scale(1);
transform: scale(1);
}
#sample{
overflow: hidden;
-webkit-transition: transform 0.125s;
-moz-transition: transform 0.125s;
transition: transform 0.125s;
-webkit-transform: scale(0);
-moz-transform: scale(0);
transform: scale(0);
}
<input type="checkbox" id="click"/>
<label for="click"> Click me </label>
<div id="sample">
<h1>Hello World main headinh</h1>
<h2>Hello World sub heading</h2>
<p>Hello world paragraph</p>
</div>
You could use a wrapper inside the div, and read its height.
document.getElementById('toggle').onclick = function() {
const sample = document.getElementById("sample");
const wrapper = sample.getElementsByClassName('wrapper');
if (parseInt(sample.style.height) > 0) {
sample.style.height = 0;
} else {
const style = window.getComputedStyle(wrapper[0], null);
const height = style.getPropertyValue("height");
sample.style.height = height;
}
};
#sample {
height: 0px;
overflow: hidden;
transition-duration: 1.2s;
}
<div id="sample">
<div class="wrapper">
<h1>Hello World main headinh</h1>
<h2>Hello World sub heading</h2>
<p>Hello world paragraph</p>
</div>
</div>
<button id="toggle">Toggle</button>
I think what you want is
var s = document.getElementById('sample');
s.style.height = s.offsetHeight + 'px';
P.S. It should be noted that offsetHeight includes vertical padding and borders. And is rounded to an integer.
More info at: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight
You can use JS to get the height and height of an html tag.
var example = document.getElementById("yourTag").offsetHeight;
Then you can do:
document.getElementById("sample").style.height = example;

Change the content of a style class

I use css to have animation (using transition) in a div. The content of the div is dynamical (can be 1 line or multiple lines).
I have 2 classes I switch to get the animation effect:
.class1{
max-height: 200px;
}
.class2{
max-height: 0;
}
My problem occurs when the I have only 1 line in the div, so there's "delay" until the 1 line disappears (because the height changes from 200px to 0 while the actual size is only 30px).
I tried to use element.style.maxHeight = elem.offsetHeight + "px" to set the max-height size but it didn't work so I want to change max-height in the class (class1 or class2) to fit the actual size.
How can I change the content of a class (I don't want to have: "style = max-height: 100px" in my div)?
You could use the transition in combination with the transform.
Not only because solve your problem, but also because has better performance then change the height.
Doesn't make sense changing the height and using CSS, in terms of performance you can use JS, it is the same.
I did an example for you on jsfiddle because on here seems to do not work.
jQuery(document).ready(function() {
jQuery('.click-me').on('click', function() {
var $this = jQuery(this);
if ($this.hasClass('show')) {
jQuery(this).removeClass('show').addClass('hide');
}
});
});
.hide {
transform: translateY(-100%);
z-index: -1;
}
.show {
transform: translateX(0);
z-index: 0;
}
.animate {
transition: transform 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="parent">
<div class="click-me animate show">
<div>one line</div>
</div>
</div>

Jquery slide effect not working as it should

Hi im trying to make a slide effect for a certain text and the sliding effect ads something like additional margin.'
The effect: like margin or something,
the framework is http://materializecss.com/.
The question: Is there any possibility to hide this "margin"?
$(document).ready(function(){
$('.collapse1').hover(function(){
$('.collapsetext1').animate({
width: 'show',
}, 444);
});
});
"Like margin or something".
I dont quite know what this means. Put if you are animating elements on based on "hover", you don't require jQuery.
.collapse1 {
margin: 10px;
transition: margin .5s linear;
}
.collapse1:hover {
margin: 0px;
}
This will transition the margin on .callapse1 between 10px and 0px when it's hovered :)
If you just want to make a sliding effect for the text without having any margin ,
you really do not need to rely on jQuery :(Pure JS is enough with a little bit of CSS)
<!DOCTYPE html>
<html>
<head>
<style>
div#box1 {
width: 400px;
position: absolute;
top: 50px;
left: -400px;
}
</style>
<script>
function slideIn(el){
var elem = document.getElementById(el);
elem.style.transition = "left 0.5s ease-in 0s";
elem.style.left = "0px";
}
function slideOut(el){
var elem = document.getElementById(el);
elem.style.transition = "left 0.5s ease-out 0s";
elem.style.left = "-400px";
}
</script>
</head>
<body>
<button onclick="slideIn('box1');">slide in</button>
<button onmouseover="slideOut('box1');">slide out</button>
<div id="box1">Content in box 1 ...</div>
</body>
</html>
You can now call the functions any time not necessarily on a button click.
So yeah , good to go .
You can refer this (video tutorial) link if you want an explanation .
You can use jquery selector .css to change any css style
$(document).ready(function() {
$('.collapse1').hover(function() {
$('.collapsetext1').css("margin", "");
});
});

Fade In and Fade Out JavaScript function for all the content div on main page, on click

I'm currently working on a web site and I need help for an opacity transition. My problem is, I want to put all my content divs on my main page and depending on which "section" the user want by clicking on my navigating bar (top of the page), the corresponding div will fade in while the current div will fade out. I thought about using jQuery, but I think I prefer doing that by myself. Here's my code (which doesn't work, obviously):
function ShowDiv(id) {
var activeDiv = document.getElementById(id);
var divs = document.getElementsByClassName("contentDiv");
for (var i = 0; i < divs.length; i++) {
if (divs[i] != activeDiv) {
divs[i].style.opacity = "0";
divs[i].style.setProperty("-webkit-transition", "all 1s ease-in-out");
divs[i].style.display = "none"
}
}
activeDiv.style.position = "block";
activeDiv.style.opacity = "1";
activeDiv.style.setProperty("-webkit-transition", "all 1s ease-in-out");
}
The function is "triggered" when I click on my navigating bar. Depending on what is clicked, the "id" will be different. Basically, I make a variable that content the "need to be shown" div, and then the others. I want the "non selected" divs to fade out, and then the "selected" div to fade in, but that doesn't work. I searched but I didn't find any answer, but one interesting thing is that the first time it will work, but won't the second, third, etc.. time.
Any suggestions? Do I really need to use jQuery?
Sounds similar to something I did not so long ago. (I did however go the Jquery route) So if you decide to change your mind, here is a FIDDLE I created to answer pretty much the same question another poster had.
Don't let this scare you, but it uses:
$('#Hotel, #Meals').on('click', function(){
var el = $('#'+this.id+'body');
if ( el.is(':visible') ) return false;
el.animate({width:'toggle'},1000)
$('#Mealsbody, #Hotelbody').not(el).hide();
});
//#Hotels & #Meals being the ID of the content you want to bring in.
//At the moment it uses .animate, but you could change this to .fadeIn if you wanted to do so
Anyways, if you do decide to take on Jquery, you can use this as a starting point.
Good luck,
Mike
HTML
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<div class="contentDiv" id="1">1</div>
<div class="contentDiv" id="2">2</div>
<div class="contentDiv" id="3">3</div>
CSS
.contentDiv {
width:100px;
height: 100px;
background: #333;
margin-bottom: 5px;
color: #fff;
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
-webkit-transition: all 1s ease;
transition: all 1s ease
}
.remove {
opacity: 0;
height:0;
line-height:0;
padding:0;
border:none;
}
Javascript
function ShowDiv(id) {
var activeDiv = document.getElementById(id);
var divs = document.getElementsByClassName("contentDiv");
for (var i = 0; i < divs.length; i++) {
if (i != id) {
divs[i].classList.add('remove');
}else {
divs[i].classList.remove('remove');
}
}
}
Working Demo

Categories