I'm trying to use the same button to open and close a menu, I'm sure this is super simple but I'm new to the world of jQuery. I'm using the Wordpress builder 'Oxygen' if that helps. Here's my code:
The modal is an in-built feature in the website builder so I can't provide much code on that. It's basically set to trigger when element with class "open" is clicked, and close with element class "oxy-modal-close".
jQuery
jQuery("#toggle").click(function () {
jQuery('#plus').toggleClass('rotate');
jQuery('#toggle').toggleClass('open oxy-modal-close');
});
HTML
<div id="toggle" class="open">
<img id="plus" src="http://hausse.co.uk/wp-content/uploads/2021/01/plus.svg"/>
</div>
CSS
#plus {
-moz-transition: transform 1s;
-webkit-transition: transform 1s;
transition: transform 0.3s;
width: 35px;
position: fixed;
top: 20px;
right: 20px;
}
.rotate {
transform: rotate(45deg);
}
Basically on the 2nd click, the class is re-adding the class "open", which is causing the menu to flicker as the two actions are conflicting with each other. Video here - https://gph.is/g/ZnNQddo
I have tried adding a delay to the class "open", but for some reason the delay is only working on the first click - on the second it's changing class instantly. This is the code I'm trying for that.
jQuery("#toggle").click(function () {
jQuery('#plus').toggleClass('rotate');
jQuery('#toggle').toggleClass('oxy-modal-close');
var el = jQuery("#toggle");
window.setTimeout(function() {
el.toggleClass('open');
}, 500);
});
You are referencing the id again within the click - you need to reference $(this)... to toggle the class on the click
Also - you need to start with one of the states - that way it can toggle the class to the other state on each click as per the snippet (the cross icon is on the right of the snippet widow as per styling ) - now when you click it rotates as intended.
$("#toggle").click(function() {
$('#plus').toggleClass('rotate');
$(this).toggleClass('open oxy-modal-close');
});
#plus {
-moz-transition: transform 1s;
-webkit-transition: transform 1s;
transition: transform 0.3s;
width: 35px;
position: fixed;
top: 20px;
right: 20px;
}
.rotate {
transform: rotate(45deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="toggle" class="open">
<img id="plus" src="http://hausse.co.uk/wp-content/uploads/2021/01/plus.svg"/>
</div>
I've been dealing with this bug for some time and I cannot figure out what to do.
I have two simple CSS animations for opening and closing the cart.
.animate-close-cart {
animation: closeCart 0.2s 0s 1 linear forwards;
}
#keyframes closeCart {
from {
transform: translateX(-35%);
}
to {
transform: translateX(0);
}
}
.animate-open-cart {
animation: openCart 0.2s 0s 1 linear forwards;
}
#keyframes openCart {
from {
transform: translateX(0);
}
to {
transform: translateX(-35%);
}
}
upon clicking my cart button I add or remove the appropriate class:
Opening the cart:
value: function _openMiniCart() {
var mainWrapper = document.getElementById("main_wrapper");
mainWrapper.classList.remove("animate-close-cart");
mainWrapper.classList.add("animate-open-cart");
}
Closing the cart:
value: function _closeMiniCart() {
var mainWrapper = document.getElementById("main_wrapper");
mainWrapper.classList.remove("animate-open-cart");
mainWrapper.classList.add("animate-close-cart");
}
The main_wrapper block is wrapped around my entire webpage:
<body>
<div id="main_wrapper">
// header, template, page contents etc.
<div/>
</body>
Opening the cart the animation plays, the web page moves to the left showing the cart. Perfect.
Closing the cart the animation plays, the web page moves back to the right, hiding the cart.
Then...
Opening it once again, the webpage just jumps without animation to the left, showing the cart,
But closing it, it plays the animation and moves back to the right.
Every open after the first initial open always makes the webpage just immediately jump to -35%.
I don't know how to debug this or what could be causing this issue.
edit:
With CSS transform the same bug where it only offers a smooth transition on the first open cart, all other open carts jump immediately:
#main_wrapper {
transition: transform 0.2s;
}
.animate-close-cart {
transform: translateX(0);
}
.animate-open-cart {
transform: translateX(0);
}
So when you say the entire page moves, do you mean you slide the main content screen a bit out of view? Or you allow the blocks to keep everything visible on both sides if they're both available? I'm not sure I'm understanding the intent correctly but here's an example of an effect I've used in the past if it helps accomplish your goal. Otherwise a reproducible example of your pain points instead of just snippets would likely make it more clear to find you a solution. Cheers.
toggleMe = () => {
const side = document.getElementById('side-wrapper');
side.classList.toggle('slide-me');
}
#main-wrapper {
height: 80vh;
width: 80vw;
margin: 2rem;
border: red 5px dashed;
display: flex;
flex-direction: row;
overflow: hidden; /* make this visible if you want to see in action */
}
#section-wrapper, #side-wrapper {
outline: #fff 3px dashed;
outline-offset: -10px;
}
#section-wrapper {
height: 100%;
width: 100%;
background-color: #00f;
}
#side-wrapper {
height: 100%;
width: 350px;
background-color: #0f0;
margin-right: -350px;
transition: margin-right 1s ease-in-out;
}
.slide-me {
margin-right: 0 !important;
}
#example input:checked:after {
content: 'SIDE IS OPEN';
display: inline-block;
margin-left: 1rem;
color: green;
}
<label id="example">
Click to toggle side slide
<input type="checkbox" onclick="toggleMe()">
</label>
<div id="main-wrapper">
<div id="section-wrapper"></div>
<div id="side-wrapper"></div>
</div>
i'm working on a project where i have to render some components with an enter and leave animation, when a component enters the screen it has to enter form the bottom, and when it leaves, it has to do it going upwards, the desired behavior is that when i change the :is property of the component tag, the current component goes upwards and the next one comes from the bottom, the code looks like this:
<template>
<div class="home">
<transition name="section">
<component :is="activeSection"></component>
</transition>
</div>
</template>
<script>
import comp1 from './comp1';
import comp2 from './comp2';
export default {
components: {
comp1,
comp2
},
data() {
activeSection: 'comp1'
}
</script>
<style scoped>
.section-enter {
top: 100vh;
}
.section-enter-to {
top: 0vh;
}
.section-enter-active {
animation-name: 'slideIn';
animation-duration: 1s;
}
.section-leave {
top: 0vh;
}
.section-leave-active {
animation-name: 'slideOut';
animation-duration: 1s;
}
.section-leave-to {
top: -100vh;
}
#keyframes slideIn {
from {
top: 100vh;
}
to {
top: 0
}
}
#keyframes slideOut {
from {
top: 0vh;
}
to {
top: -100vh;
}
}
</style>
but the actual behavior is that the first component goes upwards but the second appears inmediatly after without animation.
if i render one at a time (not destructing one and rendering another with the same action) everything works perfectly. I dont know what is happening.
There are a few problems in your CSS.
CSS Transitions and CSS Animations
A transition can be implemented using either CSS Transitions or CSS Animations. Your CSS incorrectly mixes the two concepts in this case.
In particular, the slideIn keyframes and .section-enter/.section-enter-to rules are effectively performing the same task of moving .section into view. However, this is missing a transition rule with a non-zero time, required to animate the change, so the change occurs immediately. The same issue exists for the slideOut keyframes and leave rules.
.section-enter {
top: 100vh;
}
.section-enter-to {
top: 0;
}
.section-enter-active {
transition: .5s; /* MISSING RULE */
}
.section-leave {
top: 0;
}
.section-leave-to {
top: -100vh;
}
.section-leave-active {
transition: .5s; /* MISSING RULE */
}
Removing the keyframes, and adding the missing rules (as shown above) would result in a working CSS Transition.
demo 1
Using CSS Animations
Alternatively, you could use keyframes with CSS Animations, where the animation is applied only by the *-active rules, and no *-enter/*-leave rules are used. Note your question contained unnecessary quotes in animation-name: 'slideIn';, which is invalid syntax and would be silently ignored (no animation occurs). I use a simpler shorthand in the following snippet (animation: slideIn 1s;).
.section-enter-active {
animation: slideIn 1s;
}
.section-leave-active {
animation: slideOut 1s;
}
#keyframes slideIn {
from {
top: 100vh;
}
to {
top: 0;
}
}
#keyframes slideOut {
from {
top: 0;
}
to {
top: -100vh;
}
}
demo 2
Optimizing CSS Transitions
You could also tweak your animation performance by using translateY instead of transitioning top.
/* top initially 0 in .wrapper */
.section-leave-active,
.section-enter-active {
transition: .5s;
}
.section-enter {
transform: translateY(100%);
}
.section-leave-to {
transform: translateY(-100%);
}
demo 3
Use a Mixin
Thanks for the explanation #tony19
please use a mixin for this so the logic can be repeated easily.
Also, your slideIn and slideOut can be combined by using reverse:
#mixin animationmixin($type:'animation', $style:'', $duration:1s) {
#keyframes #{$type}-#{$style} { // register animation
0% { opacity: 1; transform: none; } // reset style
100% { #content; } // custom style
}
.#{$style} { // add '.section'
&-enter-active, &-leave-active { // add '.section-enter-active', ...
transition: #{$duration};
}
&-enter, &-leave-to {
animation: #{$type}-#{$style} #{$duration}; // use animation
}
&-leave, &-enter-to {
animation: #{$type}-#{$style} #{$duration} reverse; // use animation in reverse
}
}
}
Use it like this:
#include animationmixin($style:'section') { // set custom styling
transform: translateY(100%);
};
And like this:
#include animationmixin($style:'fade') {
opacity: 0;
transform: scale(0.9);
};
Given an element that has a CSS animation that changes opacity, why does the animation get rerun when the display property changes? How do I stop this from happening?
Example code:
HTML
<div class="foo"></div>
CSS
#-webkit-keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
.foo {
background-color: red;
width: 200px;
height: 200px;
-webkit-animation: fade-in 600ms forwards 0ms ease;
}
.hidden {
display: none;
}
JS
$(function () {
$(".btn").click(function (e) {
e.preventDefault();
$(".foo").toggleClass("hidden")
})
})
JSFiddle
The behavior I am expecting to see is that the animation runs when the DOM renders, but does not rerun when the display property is changed.
One way to do it is to add a class when the button gets clicked that overwrites the animation property.
FIDDLE
$(function () {
$(".btn").click(function (e) {
e.preventDefault();
$(".foo").addClass('finished').toggleClass("hidden")
})
})
.foo.finished {
-webkit-animation: none;
}
Another option would be to hide it with position: relative instead of changing display:
FIDDLE
.hidden {
position: absolute;
left: -99999px;
}
Curious. It seems that Chrome is acting as if changing from display: none to block was the same as adding the element to the DOM. I tried it in Firefox (jsfiddle), and it works as one would expect. IE, however, does the same as Chrome. One could think that Firefox's behaviour is the correct, but, thinking about it, display: none is like taking the node form the layout and rendering trees, so, if the page loads with the object with display: none, and then it changes to block, I would expect to see the animation.
#keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
.foo {
background-color: red;
width: 200px;
height: 200px;
animation: fade-in 3000ms forwards 0ms ease;
}
.hidden {
display: none;
}
this is how you can do that.. its a bit hacky but you can remove that class and add another class that doesn't have the animation. $("#my_foo").removeClass("foo").addClass("your_foo");
http://jsfiddle.net/s68yf/6/
I have a CSS3 animation that needs to be restarted on a click. It's a bar showing how much time is left. I'm using the scaleY(0) transform to create the effect.
Now I need to restart the animation by restoring the bar to scaleY(1) and let it go to scaleY(0) again.
My first attempt to set scaleY(1) failed because it takes the same 15 seconds to bring it back to full length. Even if I change the duration to 0.1 second, I would need to delay or chain the assignment of scaleY(0) to let the bar replenishment complete.
It feels too complicated for such a simple task.
I also found an interesting tip to restart the animation by removing the element from the document, and then re-inserting a clone of it:
http://css-tricks.com/restart-css-animation/
It works, but is there a better way to restart a CSS animation?
I'm using Prototype and Move.js, but I'm not restricted to them.
No need in timeout, use reflow to apply the change:
function reset_animation() {
var el = document.getElementById('animated');
el.style.animation = 'none';
el.offsetHeight; /* trigger reflow */
el.style.animation = null;
}
#animated {
position: absolute;
width: 50px; height: 50px;
background-color: black;
animation: bounce 3s ease-in-out infinite;
}
#keyframes bounce {
0% { left: 0; }
50% { left: calc( 100% - 50px ); }
100% { left: 0; }
}
<div id="animated"></div>
<button onclick="reset_animation()">Reset</button>
Just set the animation property via JavaScript to "none" and then set a timeout that changes the property to "", so it inherits from the CSS again.
Demo for Webkit here: http://jsfiddle.net/leaverou/xK6sa/
However, keep in mind that in real world usage, you should also include -moz- (at least).
#ZachB's answer about the Web Animation API seems like "right"™ way to do this, but unfortunately seems to require that you define your animations through JavaScript. However it caught my eye and I found something related that's useful:
Element.getAnimations() and Document.getAnimations()
The support for them is pretty good as of 2021.
In my case, I wanted to restart all the animations on the page at the same time, so all I had to do was this:
const replayAnimations = () => {
document.getAnimations().forEach((anim) => {
anim.cancel();
anim.play();
});
};
But in most cases people will probably want to select which animation they restart...
getAnimations returns a bunch of CSSAnimation and CSSTransition objects that look like this:
animationName: "fade"
currentTime: 1500
effect: KeyframeEffect
composite: "replace"
pseudoElement: null
target: path.line.yellow
finished: Promise {<fulfilled>: CSSAnimation}
playState: "finished"
ready: Promise {<fulfilled>: CSSAnimation}
replaceState: "active"
timeline: DocumentTimeline {currentTime: 135640.502}
# ...etc
So you could use the animationName and target properties to select just the animations you want (albeit a little circuitously).
EDIT
Here's a handy function that might be more compatible using just Document.getAnimations, with TypeScript thrown in for demonstration/fun:
// restart animations on a given dom element
const restartAnimations = (element: Element): void => {
for (const animation of document.getAnimations()) {
if (element.contains((animation.effect as KeyframeEffect).target)) {
animation.cancel();
animation.play();
}
}
};
Implement the animation as a CSS descriptor
Add the descriptor to an element to start the animation
Use a animationend event handler function to remove the descriptor when the animation completes so that it will be ready to be added again next time you want to restart the animation.
HTML
<div id="animatedText">
Animation happens here
</div>
<script>
function startanimation(element) {
element.classList.add("animateDescriptor");
element.addEventListener( "animationend", function() {
element.classList.remove("animateDescriptor");
} );
}
</script>
<button onclick="startanimation(
document.getElementById('animatedText') )">
Click to animate above text
</button>
CSS
#keyframes fadeinout {
from { color: #000000; }
25% {color: #0000FF; }
50% {color: #00FF00; }
75% {color: #FF0000; }
to { color : #000000; }
}
.animateDescriptor {
animation: fadeinout 1.0s;
}
Try it here: jsfiddle
If you have a class for CSS3 animation, for example .blink, then you can removeClass for some element and addClass for this element thought setTimeout with 1 millisecond by click.
$("#element").click(function(){
$(this).removeClass("blink");
setTimeout(function(){
$(this).addClass("blink);
},1 // it may be only 1 millisecond, but it's enough
});
You can also use display property, just set the display to none.
display:none;
and the change backs it to block (or any other property you want).
display:block;
using JavaScript.
and it will work amazingly.
The Animation API gives you full control over when and what to play, and is supported by all modern browsers (Safari 12.1+, Chrome 44+, Firefox 48+, Edge 79+) .
const effect = new KeyframeEffect(
el, // Element to animate
[ // Keyframes
{transform: "translateY(0%)"},
{transform: "translateY(100%)"}
],
{duration: 3000, direction: "alternate", easing: "linear"} // Keyframe settings
);
const animation = new Animation(effect, document.timeline);
animation.play();
Demo: https://jsfiddle.net/cstz9L8v/
References:
https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect
https://developer.mozilla.org/en-US/docs/Web/API/Animation
There is an answer on MDN, which is similar to the reflow approach:
<div class="box">
</div>
<div class="runButton">Click me to run the animation</div>
#keyframes colorchange {
0% { background: yellow }
100% { background: blue }
}
.box {
width: 100px;
height: 100px;
border: 1px solid black;
}
.changing {
animation: colorchange 2s;
}
function play() {
document.querySelector(".box").className = "box";
window.requestAnimationFrame(function(time) {
window.requestAnimationFrame(function(time) {
document.querySelector(".box").className = "box changing";
});
});
}
If you create two identical sets of keyframes, you can "restart" the animation by swapping between them:
function restart_animation(element) {
element.classList.toggle('alt')
}
#keyframes spin1 {
to { transform: rotateY(360deg); }
}
#keyframes spin2 {
to { transform: rotateY(360deg); }
}
.spin {
animation-name: spin1;
animation-duration: 2s;
}
.alt {
animation-name: spin2;
}
div {
width: 100px;
background: #8CF;
padding: 5px;
}
<div id=_square class=spin>
<button onclick="restart_animation(_square)">
Click to restart animation
</button>
</div>
On this page you can read about restarting the element animation: Restart CSS Animation (CSS Tricks)
Here is my example:
<head>
<style>
#keyframes selectss
{
0%{opacity: 0.7;transform:scale(1);}
100%{transform:scale(2);opacity: 0;}
}
</style>
<script>
function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='selectss 2s ease-out';
var newone = elm.cloneNode(true);
elm.parentNode.replaceChild(newone, elm);
}
</script>
</head>
<body>
<div id="circle" style="height: 280px;width: 280px;opacity: 0;background-color: aqua;border-radius: 500px;"></div>
<button onclick="animation()"></button>
</body>
But if you want to you can just remove the element animation and then return it:
function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='';
setTimeout(function () {elm.style.animation='selectss 2s ease-out';},10)
}
setInterval(() => {
$('#XMLID_640_').css('animation', 'none')
setTimeout(() => {
$('#XMLID_640_').css('animation', '')
}, 3000)
}, 13000)
Create a second "keyframe#" which restarts you animation, only problem with this you cannot set any animation properties for the restarting animation (it just kinda pops back)
HTML
<div class="slide">
Some text..............
<div id="slide-anim"></div>
</div><br>
<button onclick="slider()"> Animation </button>
<button id="anim-restart"> Restart Animation </button>
<script>
var animElement = document.getElementById('slide-anim');
document.getElementById('anim-restart').addEventListener("mouseup", restart_slider);
function slider() {
animElement.style.animationName = "slider"; // other animation properties are specified in CSS
}
function restart_slider() {
animElement.style.animation = "slider-restart";
}
</script>
CSS
.slide {
position: relative;
border: 3px black inset;
padding: 3px;
width: auto;
overflow: hidden;
}
.slide div:first-child {
position: absolute;
width: 100%;
height: 100%;
background: url(wood.jpg) repeat-x;
left: 0%;
top: 0%;
animation-duration: 2s;
animation-delay: 250ms;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(.33,.99,1,1);
}
#keyframes slider {
to {left: 100%;}
}
#keyframes slider-restart {
to {left: 0%;}
}
Note that with React, clearing the animation like this, a codesandbox I found helps.
Example I used in my code:
function MyComponent() {
const [shouldTransition, setShouldTransition] = useState(true);
useEffect(() => {
setTimeout(() => {
// in my code, I change a background image here, and call this hook restart then animation,
// which first clears the animationName
setShouldTransition(false);
}, timeout * 1000);
}, [curr]);
useEffect(() => {
// then restore the animation name after it was cleared
if (shouldTransition === false) {
setShouldTransition(true);
}
}, [shouldTransition]);
return (
<div
ref={ref2}
style={{
animationName: shouldTransition ? "zoomin" : "",
}}
/>
);
}
I found out a simple solution today. Using the example provided in this answer, you can just append the element again to the body:
function resetAnimation() {
let element = document.getElementById('animated');
document.body.append(element);
}
#animated {
position: absolute;
width: 50px;
height: 50px;
background-color: LightSalmon;
animation: bounce 3s ease-in-out infinite;
}
#keyframes bounce {
0% {left: 0;}
50% {left: calc(100% - 50px);}
100% {left: 0;}
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="animated"></div>
<button onclick="resetAnimation()">Reset</button>
</body>
</html>
Using Chrome's developer tools, the append does not actually append the element to the body and just replace it, probably because the same reference to the element is used.