This question already has answers here:
CSS Transition fails on jQuery .load callback
(4 answers)
Closed 6 years ago.
transition opacity from 0 to 1 is not working. here is my code: https://jsfiddle.net/ax4aLhjq/19/
//html
<div id="a">
<div style="height:20px"></div>
</div>
//css
#a{
width:200px;
background-color:salmon;
margin:0px;
padding:0px;
height:200px;
overflow: auto;
}
#a .item{
margin:0px 5px;
background-color:teal;
padding:10px;
color:white;
opacity:0;
transition:opacity .5s ease-in-out;
}
//js
function add(){
var div = document.createElement("div");
div.className ="item";
var newtext = document.createTextNode("aa");
div.appendChild(newtext);
document.querySelector("#a").appendChild(div);
var separator = document.createElement("div");
separator.style.height="10px";
document.querySelector("#a").appendChild(separator);
//apply opacity
div.style.opacity=1;
}
setInterval(add,3000);
Am I doing something wrong?
I've found the answer here: https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/
It appears that when an element is added, repaint is needed and somehow the browser is trying to optimize the computation, and is doing the opacity=0 and opacity=1 in the same cycle.
The solution is to use getComputedStyle : https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
" getComputedStyle() in combination with accessing a property value
actually flushes all pending style changes and forces the layout
engine to compute our ’s current state"
var elem = document.createElement("div");
document.body.appendChild(elem);
// Make the element fully transparent.
elem.style.opacity = 0;
// Make sure the initial state is applied.
window.getComputedStyle(elem).opacity;
// Fade it in.
elem.style.opacity = 1;
Problem:
You are setting the opacityto 1 the same time you were creating the element.
Solution:
You have to delay tha action of showing the element, you need to set the opacity within a timeout to make the animation effect otherwise all elements will be just appended.
You can see this snippet I used a setTimoutto make the effect of the opacity animation:
//js
function add(){
var div = document.createElement("div");
div.className ="item";
var newtext = document.createTextNode("aa");
div.appendChild(newtext);
document.querySelector("#a").appendChild(div);
var separator = document.createElement("div");
separator.style.height="10px";
document.querySelector("#a").appendChild(separator);
//apply opacity
setTimeout(function(){
div.style.opacity=1;
}, 2000);
}
setInterval(add,1000);
//css
#a{
width:200px;
background-color:salmon;
margin:0px;
padding:0px;
height:200px;
overflow: auto;
}
#a .item{
margin:0px 5px;
background-color:teal;
padding:10px;
color:white;
opacity:0;
transition:opacity .5s ease-in-out;
}
<div id="a">
<div style="height:20px"></div>
</div>
Make a setTimeout like this window.setTimeout(function(){div.style.opacity=1;},17);. So the animation will effect next time.
Related
Below is a simplified version of a problem I am having with my website.
function move(){
document.getElementById("box").style.transition = "0s";
document.getElementById("box").style.top = "100px";
document.getElementById("box").style.transition = "2s";
document.getElementById("box").style.top = "0px";
}
#box{
width:100px;
height:100px;
background:red;
position:relative;
top:0px;
}
<div id="box" onclick="move()"></div>
What I want it to do is make the box instantaneously jump downwards, and then slowly move back to its starting position. I have tested each of the four lines of code inside move() separately and they work perfect. I just can't get them to run in one go.
What am I doing wrong?
It seem the code needs to delay before assigning new property that cause browser can process the request. So you need to use setTimeout() to solving this problem.
function move(){
document.getElementById("box").style.transition = "0s";
document.getElementById("box").style.top = "100px";
setTimeout(function(){
document.getElementById("box").style.transition = "2s";
document.getElementById("box").style.top = "0px";
}, 10);
}
#box{
width:100px;
height:100px;
background:red;
position:relative;
top:0px;
}
<div id="box" onclick="move()"></div>
Instead of relying on transitions, it would be better to use #keyframes and animation, so that you don't have to use dirty tricks like changing the transition duration from 0 to actual value mid-animation to achieve the jump. Below is an example that utilizes the #keyframes css features:
function move(){
document.getElementById("box").style.animation = "movement 2s";
}
#box{
width:100px;
height:100px;
background:red;
position:relative;
top:0px;
}
#keyframes movement {
from {top: 100px;}
to {top: 0px;}
}
<div id="box" onclick="move()"></div>
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.
I created this demo:
http://cristiantraina.altervista.org/boxfall/
When you click, it creates a red falling box.
The problem is that using only css there are no ways to detect the size of the screen, in fact in my demo I specify that the box has to fall for 1000px, regardless of the actual height of the screen.
This is the code of the keyframe:
#include keyframes("fall"){
to{
top: 1000px;
}
}
I can't use bottom:0px; because I wouldn't know from where to start the fall, and I didn't solve my main problem.
This is the FallBox.js script:
function FallBox(x, side, parent){
this.x = x;
this.parent = parent || $("body");
this.side = side || Math.random()*200;
this.createBox();
this.fall();
}
FallBox.prototype.createBox = function(){
box = document.createElement('div');
$box = $(box); // I hate brackets
$box.addClass("box");
$box.css({
width: this.side+"px",
height: this.side+"px",
left: this.x+"px",
top: "-"+(this.side+5)+"px"
});
this.box = $box;
}
FallBox.prototype.fall = function(){
this.parent.append(this.box);
this.box.addClass("fall");
}
I know that I could use overflow:hidden; in the parent div, but I don't think that this is the ideal solution. First because a user can have got a screen with a superior height, then because I want to the box stops when it meets the edge, as the border was ground and it shouldn't pass through.
Another solution that I found on the web, it's to use the CSSOM API, but not even mozilla developers are sure of the compatibilty of these.
So, how can I stop an animation when it meets the screen edge, since javascript fails to inject properties?
Thank you.
If you're looking for a css-only solution, you could use the css calc feature (http://caniuse.com/#feat=calc) in combination with vh (http://caniuse.com/#search=vh).
document.querySelector(".box").addEventListener("click", function() {
this.classList.toggle("is-dropped");
})
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.box {
position: absolute;
top: 0;
left: 200px;
width: 100px;
height: 100px;
background: red;
transition: top 2s;
}
.box.is-dropped {
top: calc(100vh - 100px);
}
<div class="box"></div>
You coul use the translatey() CSS transform function to shift each div up by 100% of its own height. That way you would just need 2 rules to change the value of the top position without having to worry about height in each case.
(function(d,M){
var div=d.createElement("div"),
wait=0,size;
d.body.addEventListener("click",function(){
if(!wait){
wait=1;
div=div.cloneNode(1);
div.classList.remove("go");// necessary so that newly created divs don't just get added to the bottom of the page
size=M.max(M.floor(M.random()*200),50);
div.style.height=div.style.width=size+"px";
div.style.left=M.max(M.floor(M.random()*this.offsetWidth)-size,0)+"px";
this.appendChild(div);
setTimeout(function(){
div.classList.add("go");// adding this class starts the animation.
wait=0;
},5);
}
},0);
})(document,Math);
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%}
div{
background:#000;
border:1px solid #fff;
transition:top 2s linear;
position:absolute;
top:0;
transform:translatey(-100%);
}
div.go{
top:100%;
}
ORIGINAL SOLUTION
As the height of the box is being set dynamically in your JavaScript, your CSS isn't going to know the height of each box but that doesn't stop you using the CSS calc() function to set the top position you want to animate each to, much like you currently do to set its starting top position. Here's a quick, rough example, with an alternative solution in the comments that doesn't use calc(), if you'd prefer.
var div=document.createElement("div"),
wait=0,size;
document.body.addEventListener("click",function(){
if(!wait){
wait=1;
div=div.cloneNode(0);
size=Math.max(Math.floor(Math.random()*200),50);
div.style.height=div.style.width=size+"px";
div.style.left=Math.max(Math.floor(Math.random()*this.offsetWidth)-size,0)+"px";
div.style.top="-"+size+"px";
this.appendChild(div);
setTimeout(function(){
div.style.top="calc(100% - "+size+"px)"; /* This is the important bit */
// div.style.top=document.body.offsetHeight-size+"px"; /* Alternative solution, without using calc() */
wait=0;
},5);
}
},0);
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%}
div{
background:#000;
border:1px solid #fff;
transition:top 2s linear; /* Using a transition instead of an animation */
position:absolute;
}
I am having an issue with CSS transitions and different browsers. The following Fiddle works fine on IE (the text on the right hand side correctly fades in when you hover over the items on the left and you can switch to other items with the transition still firing on every new hover), but for some reason FF and Chrome will no longer do the transition after selecting one of the items on the left.
jsfiddle link
The CSS transition code:
.FAQItemText.active, .FAQItemTextDark.active, .solutionText.active {
-webkit-transition: opacity 1500ms ease;
-moz-transition: opacity 1500ms ease;
-o-transition: opacity 1500ms ease;
transition: opacity 1500ms ease;
opacity: 1;
}
Please can anyone help me fix it to make it work on all browsers?
Thanks
A CSS transition can't repeat. if you want something that repeat, use a CSS animation
Link : http://www.w3schools.com/css/css3_animations.asp
Your code was overly complicated and there are much simpler ways of doing what you're trying to achieve. I rewrote your JS to work like it should, altered your CSS classes and rules to better suit the purpose and cleaned up your DOM a little bit also.
Check out the code below, where I have explained what changes I've made:
$(function(){
//select all the link elements and add needed event handlers:
$(".leftSlidePanel>a")
.mouseenter(preview)
.mouseleave(preview)
.click(selectSlide);
});
function preview(event){
// This is the slide the element you clicked on links to:
var targetSlide=$(event.currentTarget).attr("href");
// Let's make a selector to select the .leftItem inside the link you hovered and our target slide:
var previewedItem=$(event.currentTarget).find(".leftItem").add(targetSlide);
// and another selector to select all the .leftItems and .FAQItemTexts aparat from the ones being targeted:
var hiddenItems=$(".leftItem, .FAQItemText").not(previewedItem);
// Next add or remove classes from our selected items depending on wheter it was mouseeneter or mouseout:
if(event.type=="mouseenter"){
previewedItem.addClass("previewed");
hiddenItems.addClass("hidden");
}else{
previewedItem.removeClass("previewed");
hiddenItems.removeClass("hidden");
}
}
function selectSlide(event){
// Prevent default behaviour of clicking a link:
event.preventDefault();
// Remove class selected from all .leftItems and .FAQItemTexts:
$(".leftItem, .FAQItemText").removeClass("selected");
// And add said class to targeted elements:
$(event.currentTarget).find(".leftItem").add($(event.currentTarget).attr("href")).addClass("selected");
}
.slideContentContainer {
width:70%;
height:100%;
min-width:1050px;
margin-left:auto;
margin-right:auto;
padding-top:80px;
}
.leftSlidePanel {
float:left;
width:30%;
padding-top:29px;
}
.rightSlidePanel {
float:right;
width:70%;
padding-top:29px;
}
.leftItem {
color: transparent;
width: 100%;
margin-top:0;
text-align: left;
padding-bottom: 23px;
font-family:"Helvetica W01 Cn" !important;
}
.leftItem h4 {
margin: 0;
padding-top:5px;
}
/* this is how your links on the left will look like when they're selected or previewed: */
.leftItem.selected, .leftItem.previewed {
color: rgb(227, 114, 22);
}
.dark.selected {
color: rgb(49, 49, 50);
}
/* This is the style your FAQItemText has normally and when another item is being previewed: */
.FAQItemText, .FAQItemText.selected.hidden {
width: 95%;
opacity: 0;
float:right;
font-size: 20px;
padding-top:29px;
text-align: justify;
color: rgb(227, 114, 22);
font-family:"Helvetica W01 Cn" !important;
display:none;
}
/* Instead of creating another class with almost the same rules as another, create a subclass: */
.FAQItemText.dark {
color: rgb(49, 49, 50);
}
/* This is the style your FAQItemText will have when it's either selected or previewed: */
.FAQItemText.selected, .FAQItemText.previewed{
opacity: 1;
display:block;
/* Firefox and IE support animations without vendor prefixes, so -webkit- is the only one you'll need */
-webkit-animation: fade 1.5s 1;
animation: fade 1.5s 1;
}
/* using display:block wile transitioning opacity can often doesn't give you the effect you'd want, but this can be fixed by using an animation instead: */
#-webkit-keyframes fade{
0% {display:none; opacity:0;}
1% {display:block; opacity:0;}
100% {display:block; opacity:1;}
}
#-keyframes fade{
0% {display:none; opacity:0;}
1% {display:block; opacity:0;}
100% {display:block; opacity:1;}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="section" id="faqs">
<div class="slideContentContainer">
<div class="leftSlidePanel">
<div id="menuItem11" class="leftItem selected"><h4>1</h4></div>
<div id="menuItem12" class="leftItem"><h4>2</h4></div>
<div id="menuItem13" class="leftItem"><h4>3</h4></div>
<div id="menuItem14" class="leftItem"><h4>4</h4></div>
<div id="menuItem15" class="leftItem"><h4>5</h4></div>
<div id="menuItem16" class="leftItem"><h4>6</h4></div>
</div>
<div class="rightSlidePanel">
<div id="slideItem11" class="FAQItemText selected">BLAH 1 BLAH.</div>
<div id="slideItem12" class="FAQItemText">BLAH 2 BLAH.</div>
<div id="slideItem13" class="FAQItemText">BLAH 3 BLAH.</div>
<div id="slideItem14" class="FAQItemText">BLAH 4 BLAH.</div>
<div id="slideItem15" class="FAQItemText">BLAH 5 BLAH.</div>
<div id="slideItem16" class="FAQItemText">BLAH 6 BLAH.</div>
</div>
</div>
</div>
Thank you all for your suggestions, after a while of googling I came across this which seems to do the trick.
So I changed my js code from this:
function ShowSlide(slide, slideItem) {
//Reset page
resetAllHighlights(slide);
//Show needed ones.
jQuery('#slideItem' + slide + slideItem).show();
jQuery('#slideItem' + slide + slideItem).addClass("active");
jQuery('#menuItem' + slide + slideItem).addClass("itemSelected");
}
To this:
function ShowSlide(slide, slideItem) {
//Reset page
resetAllHighlights(slide);
//Show needed ones.
jQuery('#slideItem' + slide + slideItem).show(0);
jQuery('#slideItem' + slide + slideItem).addClass("active");
jQuery('#menuItem' + slide + slideItem).addClass("itemSelected");
}
And it now works in all browsers.
I got some elements, and when an event is triggered one of them is removed or added to the DOM, when this happens the rest of the elements moves around to find their right place on the DOM, what I want is to animate that movement.
Any ideas? I would like to only use CSS if it's possible.
Note that when clicked the button, the element 2 goes off or on and the others move's, I want that movement animated.
Here is my code
$('button').click(function(){
element = $('#dos').is(":visible");
if(!element){
$('#dos').show();
}
else{$('#dos').hide();}
})
section{
margin:auto;
display:block;
text-align:center;
}
#uno{
width:200px;
background:rgba(255,229,0,1.00);
height:100px;
display:inline-block;
}
#dos{
width:200px;
background:rgba(0,255,60,1.00);
height:100px;
display:inline-block;
}
#tres{
width:200px;
background:rgba(232,0,255,1.00);
height:100px;
display:inline-block;
}
button{
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<section>
<div id="uno">1</div>
<div id="dos">2</div>
<div id="tres">3</div><br>
<button>Click</button>
</section>
If you want it to be done in CSS, then work with .addClass()/.removeClass() instead of .show() and .hide(). Learn about keyframes – it's easy, intuitive and gives full control over CSS animations. It's as easy as:
#keyframes hide-animation {
to {
width: 0%;
visibility: hidden;
}
}
.hidden {
animation: hide-animation .3s linear forwards;
}
You can bind any animation you want to the class you are adding. Here's your JSFiddle with working hide animation.
It's hard for me to give an exact answer without knowing what kind of movement you want but I'll take a stab at it.
One general solution is to put the element you are hiding/showing in a container div, and then animate the width or height of the container div. Let me see if I can give you an example for vertical:
HTML:
<div id="uno">1</div>
<div id="dos-container">
<div id="dos">2</div>
</div>
<div id="tres">3</div>
CSS:
#uno{
height:100px;
}
#dos{
height:100px;
}
#dos-container{ /* same height as dos if starting visible, if starting hidden set to 0*/
height:100px;
}
#tres{
height:100px;
}
JS(with jquery):
$('button').click(function(){
element = $('#dos').is(":visible");
if(!element){
//animate container height to match content div height
$('#dos-container').animate({height: "100px")},500); //500 is the speed of the animation
//show content in container
$('#dos').show();
}
else{
//animate container height to 0
$('#dos-container').animate({height: "0px")},500);
//then hide content div
$('#dos').hide();
}
})