How do I stop CSS transitions from running the first time? - css

My site's menu changes to an off-canvas menu when the screen width is < 768px. The menu can then be triggered by a click, and it translates in and out using a CSS transition.
The problem is when a browser is > 768px, then gets resized to < 768px. The menu quickly transitions out instead of initially being off-canvas.
You can see an example here: http://codepen.io/anon/pen/RPoQzO (Code below)
Make the preview section really wide, and you'll see the white box fill the width
Resize the section narrow and you'll see it slide out. I don't want that - I just want it to be instantly gone.
Clicking on the green area, you'll see the desired transition effect.
(This is, of course a bastardized representation, but it exemplifies the problem)
I want to solve this (if possible) with just CSS. I don't want to add a Javascript listener for resizing. I'd rather have the effect continue, than use Javascript.
Edit: Here's the code:
<div id="menu">
This is the menu
</div>
And the CSS
body{
background:green;
}
#menu{
background:white;
}
#media(max-width:768px){
#menu{
transform:translateX(-100%);
-webkit-transform:translateX(-100%);
transition:transform 0.3s;
-webkit-transition:-webkit-transform 0.3s;
}
#menu.in{
transform:translateX(0);
-webkit-transform:translateX(0);
}
}

You can try to achieve this with one more helper class that would set a transition:
#menu {
background:white;
}
#media (max-width: 768px) {
#menu {
transform:translateX(-100%);
-webkit-transform:translateX(-100%);
}
#menu.clicked {
transition: transform 0.3s;
-webkit-transition:-webkit-transform 0.3s;
}
#menu.in {
transform:translateX(0);
-webkit-transform:translateX(0);
}
}
and JS part:
$(window).on('click',function() {
$("#menu").toggleClass('in').addClass('clicked');
setTimeout(function() {
$("#menu").removeClass('clicked');
}, 100);
});
Note, that you need to remove helper class after some short timeout, so that the transition is only active in case of click, but not when window is resized.
Demo: http://codepen.io/anon/pen/aOBYYP

Moving transition:-webkit-transform 0.3s; transition:transform 0.3s; from inside the media query to the standard #menu seems to work, as shown:
From this:
#menu{
background:white;
}
#media(max-width:768px){
#menu{
transform:translateX(-100%);
-webkit-transform:translateX(-100%);
transition:transform 0.3s;
-webkit-transition:-webkit-transform 0.3s;
}
#menu.in{
transform:translateX(0);
-webkit-transform:translateX(0);
}
}
To this:
#menu{
background:white;
transition:transform 0.3s;
-webkit-transition:-webkit-transform 0.3s;
}
#media(max-width:768px){
#menu{
transform:translateX(-100%);
-webkit-transform:translateX(-100%);
}
#menu.in{
transform:translateX(0);
-webkit-transform:translateX(0);
}
}
This is because there is no transition property applied when the screen is wider than 768px; so it will suddenly jump instead of smoothly changing.

Now that I understand the question more clearly, you could try this:
<style>
body{
background:green;
}
#menu{
background:white;
transform:translateX(-100%);
-webkit-transform:translateX(-100%);
}
.out{
transform:translateX(0%)!important;
-webkit- transform:translateX(-0%)!important;
}
#media(max-width:768px)
{
#menu{
transition:transform 0.3s;
-webkit-transition:-webkit-transform 0.3s;
}
}
#media(min-width:786px)
{
#menu{
transform:translateX(0%);
-webkit- transform:translateX(-0%);
}
}
</style>
<body onclick=" if(window.outerWidth < 786){document.querySelector('#menu') .classList.toggle('out');}">
<div id="menu">
This is the menu
</div>
</body>
This method doesn't use any event listeners, and providing it's not inside a frame (where the outerWidth is not it's width), it seems to work.

Related

CSS keyframe animation not working on div element using opacity

I have div .list element with CSS below
.list{
bottom:0;
height:calc(100% - 85px);
left:50%;
max-width:600px;
padding-bottom:85px;
-webkit-transform:translate(-50%,-5%);
z-index:-1;
display:flex;
width:100%;
flex-wrap:wrap;
overflow-x:hidden;
overflow-y:scroll;
position:fixed;
justify-content:center;
opacity:0;
background-color:#f1f1f1;
}
I use JavaScript to add .anim class to this element which contains animation to open .list div element on click open button
document.querySelector('.list').classList.add('anim');
.anim class should animate opening .list using CSS animation.
.anim {
-webkit-animation:openlist 200ms ease forwards;
z-index:2
}
#-webkit-keyframes openlist {
0% {
-webkit-transform:translate(-50%,-5%)
}
to {
-webkit-transform:translate(-50%,0);
opacity:1
}
}
Problem with opacity: when I use Opacity outside CSS animation and put it inside .anim class after animation definition like below:
.anim {
-webkit-animation:openlist 200ms ease forwards;
z-index:2;
opacity:1;
}
.list div is scrollable without any problem but I don't get transition effect from opacity:0 to 1.
If I use opacity inside animation like below:
to {
-webkit-transform:translate(-50%,0);
opacity:1
}
Everything is working fine but .list div is not scrolling and scrollbar is hidden.
HTML
<body class="x">
<input type="search" class="s2" placeholder="Search...">
<div class="s1"></div>
<div class="c1"></div>
<div class="list"></div>
<img src="icons/heart.svg" id="m">
</body>
BODY CSS:
.x {
width:100%;
height:100%;
margin:90px 0;
overflow-x:hidden;
overflow-y:scroll;
}
Question: How to get CSS animation working with opacity inside and keep transition effect plus .list div scrollable?
I found a solution, problem was that i used opacity instead of filter:opacity, now i get opacity effect without scrolling problems

Transitions won't work when transform:translate3d combined with opacity

On my website I have a second header that drifts down from the top when the user scrolls down the page. The original header remains absolutely positioned at the top and is scrolled out of sight as the second slides down.
Due to the Google Chrome bounce scroll effect, if the user scrolls up when the browser is already at the top of the page, they're able to see the second header hanging around outside the document. This looks very strange, and it only happens in Chrome.
I've been trying to make the second header invisible when the user scrolls back to the top and it slides back out of view. I have been attempting to do this with an opacity value of 0 set with an ease value. The problem is, I am using transform:translate3d to animate the slide-up / slide-down effect, and I can't get both opacity and transform to work in the same transition rule.
Ideally I'd like the following to work, but it won't for some reason.
.hidden-header {
position:fixed;
transform:translate3d(0,-100%,0);
background-color:red;
width:100%;
height:55px;
opacity:0;
transition: translate 0.3s, opacity 0s ease .3s;
}
body.header-dropdown .hidden-header {
transform:translate3d(0,0,0);
opacity:1;
transition: translate .5s, opacity 0s;
}
Here is a jsFiddle to show you what I mean – https://jsfiddle.net/wbmm0kL7/2/
At the moment I have had to set it to transition: all .3s which means that the opacity fades in and out as well, which I want to avoid.
Here is a picture of my website with the problem on Chrome I am trying to solve. Notice that the second header and the nav menu are visible when scrolling against the edge of the viewport/document.
Here is the rest of my code:
HTML
<header class="header">REGULAR HEADER</header>
<div class="transform-container">
<div class="hidden-header">HIDDEN HEADER (SLIDES DOWN ON SCROLL)</div>
</div>
<div class="content"></div>
</div>
CSS
html,
body {
height:100%;
width:100%;
margin:0;
padding:0;
}
.wrapper {
background-color:orange;
min-height:100%;
}
.header {
position:absolute;
width:100%;
height:55px;
background-color:pink;
}
.hidden-header {
position:fixed;
transform:translate3d(0,-100%,0);
background-color:red;
width:100%;
height:55px;
opacity:0;
transition: all .3s;
}
body.header-dropdown .hidden-header {
transform:translate3d(0,0,0);
opacity:1;
transition: all .5s;
}
.content {
height:2000px;
}
jQuery
jQuery(document).ready(function($) {
$(window).scroll(function() {
if ($(this).scrollTop() > 200) {
$('body').addClass('header-dropdown');
} else {
$('body').removeClass('header-dropdown');
}
});
});
As per my comment, you have a typo in your CSS transitions rule. You cannot transition individual transform components. Instead, use transition: transform 0.5s; for example.
To achieve the effect of the hidden header appearing immediately, you set the transition duration of opacity to 0s when .header-dropdown is added. To achieve the effect of the hidden header hiding after the transform is done transitioning, you set the transition delay of opacity to the transition duration used:
.hidden-header {
position:fixed;
transform:translate3d(0,-100%,0);
background-color:red;
width:100%;
height:55px;
/* Delay opacity transition when returning to ground state */
transition: transform 0.5s, opacity 0s 0.5s;
opacity: 0;
}
body.header-dropdown .hidden-header {
transform:translate3d(0,0,0);
opacity: 1;
transition: transform 0.5s 0s, opacity 0s;
}
See your fixed fiddle here: https://jsfiddle.net/teddyrised/wbmm0kL7/3/
Note that the first numeric value of the transition shorthand is always the transition-duration, while the second numeric value is the transition-delay

CSS3: transform flickers on FF

I try to animate a pseudo element width css3. Everything is ok. But on Firefox (43.0.3) at the end of the animation the font flickers:
div {
width:500px;
height:500px;
color:red;
font-size:100px;
background:black;
transform:matrix(1.0001,0.00,0.00,1.0001,0,0);
}
div:before {
content:"test";
font-size:100px;
color:white;
margin:0 0 0 200px;
display:block;
animation:test 2s ease-in-out 1s both;
transform:matrix(1.0001,0.00,0.00,1.0001,0,0);
}
#keyframes test {
0% {
transform:matrix(1.0001,0.00,0.00,1.0001,0,0);
}
50% {
transform:matrix(1.50,0.00,0.00,1.50,0,0);
}
100% {
transform:matrix(1.0001,0.00,0.00,1.0001,0,0);
}
}
<div></div>
[Edit] I didnt get the point how to link to jsfiddle. so heres the link:
jsfiddle.net SLASH focgzeye
Anyone could help?
Try to add line-height same as font-size:
line-height:100px;
Tested on Firefox 43.0.1 but with the same flicker problem.
Here the jsfiddle update.

How to prevent a CSS keyframe animation from running on page load?

I have a div in which I animate the content:
#container {
position: relative;
width: 100px;
height: 100px;
border-style: inset;
}
#content {
visibility: hidden;
-webkit-animation: animDown 1s ease;
position: absolute;
top: 100px;
width: 100%;
height: 100%;
background-color: lightgreen;
}
#container:hover #content {
-webkit-animation: animUp 1s ease;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes animUp {
0% {
-webkit-transform: translateY(0);
visibility: hidden;
opacity: 0;
}
100% {
-webkit-transform: translateY(-100%);
visibility: visible;
opacity: 1;
}
}
#-webkit-keyframes animDown {
0% {
-webkit-transform: translateY(-100%);
visibility: visible;
opacity: 1;
}
100% {
-webkit-transform: translateY(0);
visibility: hidden;
opacity: 0;
}
}
<div id="container">
<div id="content"></div>
</div>
On hover, the content slides into the container div.
When I refresh the page and the page loads, the #content's animDown animation will run, and I'd prefer it to run only after a hover event.
Is there a way to do this pure CSS, or I have to figure something out in JS?
http://jsfiddle.net/d0yhve8y/
I always set preload class to body with animation time value 0 and its working pretty well. I have some back going transitions so I have to remove load animation to them too. I solved this by temporary setting animation time to 0. You can change transitions to match yours.
HTML
... <body class="preload">...
CSS is setting animation to 0s
body.preload *{
animation-duration: 0s !important;
-webkit-animation-duration: 0s !important;
transition:background-color 0s, opacity 0s, color 0s, width 0s, height 0s, padding 0s, margin 0s !important;}
JS will remove class after some delay so animations can happen in normal time :)
setTimeout(function(){
document.body.className="";
},500);
Solution 1 - Add down animation on first hover
Probably the best option is to not put the down animation on until the user has hovered over the container for the first time.
This involves listening to the mouseover event then adding a class with the animation at that point, and removing the event listener. The main (potential) downside of this is it relies on Javascript.
;(function(){
var c = document.getElementById('container');
function addAnim() {
c.classList.add('animated')
// remove the listener, no longer needed
c.removeEventListener('mouseover', addAnim);
};
// listen to mouseover for the container
c.addEventListener('mouseover', addAnim);
})();
#container {
position:relative;
width:100px;
height:100px;
border-style:inset;
}
#content {
position:absolute;
top:100px;
width:100%;
height:100%;
background-color:lightgreen;
opacity:0;
}
/* This gets added on first mouseover */
#container.animated #content {
-webkit-animation:animDown 1s ease;
}
#container:hover #content {
-webkit-animation:animUp 1s ease;
animation-fill-mode:forwards;
-webkit-animation-fill-mode:forwards;
}
#-webkit-keyframes animUp {
0% {
-webkit-transform:translateY(0);
opacity:0;
}
100% {
-webkit-transform:translateY(-100%);
opacity:1;
}
}
#-webkit-keyframes animDown {
0% {
-webkit-transform:translateY(-100%);
opacity:1;
}
100% {
-webkit-transform:translateY(0);
opacity:0;
}
}
<div id="container">
<div id="content"></div>
</div>
Solution 2 - play animation hidden
Another way around this is to initially hide the element, make sure the animation plays while it is hidden, then make it visible. The downside of this is that the timing could be slightly off and it is made visible too early, and also the hover isn't available straight away.
This requires some Javascript which waits for the length of the animation and only then makes #content visible. This means you also need to set the initial opacity to 0 so it doesn't appear on load and also remove the visibility from the keyframes - these aren't doing anything anyway:
// wait for the animation length, plus a bit, then make the element visible
window.setTimeout(function() {
document.getElementById('content').style.visibility = 'visible';
}, 1100);
#container {
position:relative;
width:100px;
height:100px;
border-style:inset;
}
#content {
visibility:hidden;
-webkit-animation:animDown 1s ease;
position:absolute;
top:100px;
width:100%;
height:100%;
background-color:lightgreen;
opacity:0;
}
#container:hover #content {
-webkit-animation:animUp 1s ease;
animation-fill-mode:forwards;
-webkit-animation-fill-mode:forwards;
}
#-webkit-keyframes animUp {
0% {
-webkit-transform:translateY(0);
opacity:0;
}
100% {
-webkit-transform:translateY(-100%);
opacity:1;
}
}
#-webkit-keyframes animDown {
0% {
-webkit-transform:translateY(-100%);
opacity:1;
}
100% {
-webkit-transform:translateY(0);
opacity:0;
}
}
<div id="container">
<div id="content"></div>
</div>
Solution 3 - Use transitions
In your scenario, you can make this CSS only by replacing the keyframes with a transition instead, so it starts with opacity:0 and just the hover has a change in opacity and the transform:
#container {
position:relative;
width:100px;
height:100px;
border-style:inset;
}
#content {
position:absolute;
top:100px;
width:100%;
height:100%;
background-color:lightgreen;
/* initial state - hidden */
opacity:0;
/* set properties to animate - applies to hover and revert */
transition:opacity 1s, transform 1s;
}
#container:hover #content {
/* Just set properties to change - no need to change visibility */
opacity:1;
-webkit-transform:translateY(-100%);
transform:translateY(-100%);
}
<div id="container">
<div id="content"></div>
</div>
Is there a way to do this pure CSS ?
Yes, absolutely : See the fork http://jsfiddle.net/5r32Lsme/2/
There is really no need for JS.
and I'd prefer it to run only after a hover event.
So you need to tell CSS what happens when it is NOT a hover event as well - in your example :
#container:not(:hover) #content {
visibility: hidden;
transition: visibility 0.01s 1s;
}
But there are two things to note:
1) The transition delay above should match your animation duration
2) You can't use the property which you use to hide the animation onLoad in the animation.
If you do need visibility in the animation, hide the animation initially like e.g.
#container:not(:hover) #content {
top: -8000px;
transition: top 0.01s 1s;
}
A sidenote:
It is recommended to put native CSS properties after prefixed ones, so it should be
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
and now there is a native transform
-webkit-transform: translateY(0);
transform: translateY(0);
If you're looking at this after 2019, a better solution is this:
let div = document.querySelector('div')
document.addEventListener('DOMContentLoaded', () => {
// Adding timeout to simulate the loading of the page
setTimeout(() => {
div.classList.remove('prevent-animation')
}, 2000)
document.querySelector('button').addEventListener('click', () => {
if(div.classList.contains('after')) {
div.classList.remove('after')
} else {
div.classList.add('after')
}
})
})
div {
background-color: purple;
height: 150px;
width: 150px;
}
.animated-class {
animation: animationName 2000ms;
}
.animated-class.prevent-animation {
animation-duration: 0ms;
}
.animated-class.after {
animation: animation2 2000ms;
background-color: orange;
}
#keyframes animationName {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: purple;
}
}
#keyframes animation2 {
0% {
background-color: salmon;
}
50% {
background-color: green;
}
100% {
background-color: orange;
}
}
<div class="animated-class prevent-animation"></div>
<button id="btn">Toggle between animations</button>
Having had to solve a similar challenge, a neat CSS-only trick morewry posted already back in 2013 is to create an animation that initially is in a paused play-state on a keyframe hiding the element:
#content {
animation:animDown 1s ease, hasHovered 1ms paused;
animation-fill-mode: forwards; /* for both animations! */
}
#container:hover #content {
animation:animUp 1s ease, hasHovered 1ms;
}
/* hide #content element until #container has been hovered over */
#keyframes hasHovered {
0% { visibility: hidden; } /* property has to be removed */
100% { visibility: visible; } /* from the other animations! */
}
When hovering, the very brief animated transformation is applied and stays in the 100%-keyframe-state even after mouse-leave thanks to the animation-fill-mode.
For how to set animation sub-properties with multiple animations, see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations#setting_multiple_animation_property_values
This is not pure CSS but maybe someone will stumble across this thread as I did:
In React I solved this by setting a temporary class in ComponentDidMount() like so:
componentDidMount = () => {
document.getElementById("myContainer").className =
"myContainer pageload";
};
and then in css:
.myContainer.pageload {
animation: none;
}
.myContainer.pageload * {
animation: none;
}
If you are not familiar the " *" (n.b. the space) above means that it applies to all descendents of the element as well. The space means all descendents and the asterisk is a wildcard operator that refers to all types of elements.
It's always better a solution without relying on javascript.
The ones with CSS mentioned here are ok. The idea of hiding when not on mouse hover is fine for some situations, but I noticed that if I wanted the animation to happen when the mouse moves out of the element, it wouldn't happen because of the :not(:hover) rule.
The solution I came up worked best for me, by adding a animation to the parent element, that only adds opacity at the end with the same duration. Easier shown than explain:
I grabbed the fiddle made by #sebilasse and #9000 and I added the below code there:
https://jsfiddle.net/marcosrego/vqo3sr8z/2/
#container{
animation: animShow 1s forwards;
}
#keyframes animShow {
0% {
opacity: 0;
}
99% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Rotation animation that (appears) not to run until needed.
The CSS below allows for up and down arrows for showing menu items.
The animation does not appear to run on page load, but it really does.
#keyframes rotateDown {
from { transform: rotate(180deg); }
to { transform: rotate(0deg); }
}
#keyframes rotateUp {
from { transform: rotate(180deg); }
to { transform: rotate(0deg); }
}
div.menu input[type='checkbox'] + label.menu::before {
display :inline-block;
content : "▼";
color : #b78369;
opacity : 0.5;
font-size : 1.2em;
}
div.menu input[type='checkbox']:checked + label.menu::before {
display : inline-block;
content : "▲";
color : #b78369;
opacity : 0.5;
font-size : 1.2em;
}
div.menu input[type='checkbox'] + label.menu {
display : inline-block;
animation-name : rotateDown;
animation-duration : 1ms;
}
div.menu input[type='checkbox']:checked + label.menu {
display : inline-block;
animation-name : rotateUp;
animation-duration : 1ms;
}
div.menu input[type='checkbox'] + label.menu:hover {
animation-duration : 500ms;
}
div.menu input[type='checkbox']:checked + label.menu:hover {
animation-duration : 500ms;
}
From top to bottom:
Create the rotations. For this there are two... one for the down arrow and one for the up arrow. Two arrows are needed, because, after the rotation, they return to their natural state. So, the down arrow starts up and rotates down, while the up arrow starts down and rotates up.
Create the little arrows. This is a straight forward implementation of ::before
We put the animation on the label. There is nothing special, there, except that the animation duration is 1ms.
The mouse drives the animation speed. When the mouse hovers over the element, the animation-duration is set to enough time to seem smooth.
Working on my site
Building off of Tominator's answer, in React, you can apply it per component like so:
import React, { Component } from 'react'
export default class MyThing extends Component {
constructor(props) {
super(props);
this.state = {
preloadClassName: 'preload'
}
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.preloadClassName !== this.state.preloadClassName;
}
componentDidUpdate() {
this.setState({ preloadClassName: null });
}
render() {
const { preloadClassName } = this.state;
return (
<div className={`animation-class ${preloadClassName}`}>
<p>Hello World!</p>
</div>
)
}
}
and the css class:
.preload * {
-webkit-animation-duration: 0s !important;
animation-duration: 0s !important;
transition: background-color 0s, opacity 0s, color 0s, width 0s, height 0s, padding 0s, margin 0s !important;
}

CSS Help: div:hover~div does not work for div later in code

Example of page
Sorry about the confusing title, I will try to describe the issue better here.
The page has two equal dividers, when I hover over the left div, I want the opacity to change as well, I want the opacity of the second div to change concurrently. The code currently does this, however, with the right divider, the hover only changes itself and not the left divider.
I am open to new ways to approach this as well.
HTML:
<div class="wrap">
<div class="bgimage" id="left">
<div class="text">
<h1>Portfolio</h1>
</div>
</div>
<div class="bgimage" id="right">
<div class="text">
<h1>Photography</h1>
</div>
</div>
</div>
CSS:
.wrap {
width:100%;
height:100vh;
background-color:#000;
}
.text {
height:55px;
opacity:0.9;
text-align:center;
vertical-align:middle;
position:absolute;
top:0;
bottom:0;
margin:auto; width:50%;
}
.bgimage{
width:50%;
height:100vh;
opacity: 0.6;
-webkit-transition: opacity 0.8s ease-in-out;
-moz-transition: opacity 0.8s ease-in-out;
-o-transition: opacity 0.8s ease-in-out;
transition: opacity 0.8s ease-in-out;
background-size:cover;
background-repeat:no-repeat;
background-position:center;
}
#left {
float:left;
background-image:url(left.jpg);
}
#right {
float: right;
background-image:url(right.jpg);
}
#left:hover~div#right, #right:hover~div#left {
opacity: 0.3;
}
#left:hover, #right:hover {
opacity: 1;
}
Of course it will work for the first div but not the second, and you can never select a preceding element in CSS, your best shot is jQuery, take a look at this code:
$(document).ready(function () {
$('.bgimage').hover(function () {
$('.bgimage').removeClass('hovered').addClass('unhovered');
$(this).addClass('hovered').removeClass('unhovered');
});
});
Here's a FIDDLE of your example, I made minor CSS changes .. Hope it helps

Resources