I would like to repeat animation every time, when I click my button. I tried to do something like this.
const dist = document.querySelector('.dist');
document.querySelector('button').addEventListener('click', () => {
dist.classList.remove('animation');
dist.classList.add('animation');
});
.dist {
width: 100px;
height: 100px;
background: black;
margin-bottom: 30px;
}
.animation {
transform: scale(1.5);
transition: transform 3s;
}
<div class="dist"></div>
<button type="button">Trigger Animation</button>
But actually, this snippet does it only one time.
dist.classList.remove('animation');
dist.classList.add('animation');
Shouldn't this part remove state and start animating from the beginning?
Updated fiddle.
You should give the remove an extra time before adding the new class animation (just a small Timeout will do the trick) :
dist.classList.remove('animation');
setTimeout(function(){
dist.classList.add('animation');
},10);
Hope this helps.
const dist = document.querySelector('.dist');
document.querySelector('button').addEventListener('click', () => {
dist.classList.remove('animation');
setTimeout(function(){
dist.classList.add('animation');
},10);
});
.dist {
width: 100px;
height: 100px;
background: black;
margin-bottom: 30px;
}
.animation {
transform: scale(1.5);
transition: transform 3s;
}
<div class="dist"></div>
<button type="button">Trigger Animation</button>
The class changes are being batched. You should request an animation frame to add the class back to the element:
window.requestAnimationFrame(function() {
dist.classList.add('animation');
});
const dist = document.querySelector('.dist');
document.querySelector('button').addEventListener('click', () => {
dist.classList.remove('animation');
window.requestAnimationFrame(function() {
dist.classList.add('animation');
});
});
.dist {
width: 100px;
height: 100px;
background: black;
margin-bottom: 30px;
}
.animation {
transform: scale(1.5);
transition: transform 3s;
}
<div class="dist"></div>
<button type="button">Trigger Animation</button>
Docs for requestAnimationFrame
See updated Fiddle
This doesn't work because there is no time there for the animation to happen. Essentially the browser doesn't ever notice the class being removed because the element gains it back immediately after it is removed. There's no time for it to see the change so it doesn't animate. In order to get it to repeat you need to give it some time to notice, a setTimeout is a good choice for this.
Also if you want it to animate returning back to the smaller size you need to change which class has the transition timing. If you have it on the added class, once it's remove you lose the timing so it snaps back to the smaller size.
If you don't care about the animation returning, keep your css the same and change the timeout to something shorter like 100.
Try doing something like:
const dist = document.querySelector('.dist');
document.querySelector('button').addEventListener('click', () => {
if(!dist.classList.contains('animation')){
dist.classList.add('animation');
} else {
dist.classList.remove('animation');
// Add it back after 3 seconds;
setTimeout(function(){
dist.classList.add('animation');
}, 1000 * 3);
}
});
.dist {
width: 100px;
height: 100px;
background: black;
margin-bottom: 30px;
transition: transform 3s;
}
.animation {
transform: scale(1.5);
}
<div class="dist"></div>
<button type="button">Trigger Animation</button>
I had The same issue and the above answers helped me get the solution that worked for me .
the requestAnimationFrame() was adding the class before the animation is complete , and the setInterval()
was keep executing for ever after user clicks and might conflict with the next clicks so , I had tow solutions either using requestAnimationFrame() with time stamp Or use setTimeout() and clearInterval()
with the following steps :
make a separate function for adding class
function addAnimation(){
dist.classList.add('animation');
}
Inside the removing animation function call the addAnimation() inside setTimeout() and assign that into variable so we can use clearInerval() to stop it
document.querySelector('button').addEventListener('click', () => {
dist.classList.remove('animation');
animate = setTimeout(addAnimation,2000)
});
now lets go back to the addAnimation() function and add clearInerval() ,this will stop the extra execution that might cause issues.
function addAnimation(){
dist.classList.add('animation');
clearInerval(animate);
}
this way when user clicks the class is removed and after the setTimeout time the class is added (just once ) since we used clearInerval() after adding the class
NOTE :
in my case I was first adding the class to animate and then removing it .
Hope that is clear and help some one; its too late form the question publish date.
All The best!
Related
so what i want to do is to hide the element for the first 5 seconds the i want the element to show after 5 secs of page load
i have tried many methods but nothing worked yet
here is my code:
<div id="main_sec_2"></div>
css
#main_sec_2{
position: relative;
margin-left: 5px;
display: none;
vertical-align: top;
}
js
function delay () {
setTimeout( function() {
window.onload = function() {
document.getElementById('main_sec_2').style.display = 'inline-block';
}
;}, 500 );
}
If you want to use JavaScript, then your JavaScript code isn't working for a few reasons.
Below is some working JavaScript code. (The explanation is below it.)
function delay() {
window.onload = function() {
setTimeout(function() {
document.getElementById("main_sec_2").style.display = "inline-block";
}, 5000);
}
}
delay();
#main_sec_2 {
position: relative;
margin-left: 5px;
display: none;
vertical-align: top;
}
<div id="main_sec_2">Division!</div>
Text in div used for visualisation purposes.
The reason this code works is:
The setTimeout delay is set to 5000, not 500. The reason this works is because the timeout is measured in milliseconds, so 5000 milliseconds is the same as 5 seconds.
(Maybe) The window.onload function has the setTimeout inside of it. This means that when the onload function is called, the setTimeout will automatically start.
(Maybe) The delay function is being called. Note that this may be because the OP didn't include the call.
In conclusion, you can use JavaScript or CSS for this issue, but this solution fixes the issue the JavaScript way.
You could simply use css-animation which does not require JS.
div {
opacity: 0;
animation-name: randomName;
animation-delay: 5s;
}
#keyframes randomName {
0% { opacity: 0; }
100% { opacity: 1; }
}
/* for visualisation purpose only */
div {
height: 50vh;
background-color: red;
}
<div id="main_sec_2"></div>
This question already has answers here:
CSS transition doesn't start/callback isn't called
(3 answers)
Closed 2 years ago.
I want to do the following: Create a div (#test) and then clone that div many times, each time it is cloned adding a css transition to it via javascript. All works well the first time, but if I try to clone a second time, and apply the css transition, then the transition doesn't work.
In this example (https://jsfiddle.net/9uL1qt6n/13/), the red square moves like it is supposed to, but the green square doesn't move, and appears at the end of the transition instantly.
Here is the javascript code that I am using:
function move(color){
let clone=document.getElementById("test").cloneNode(true);
clone.id=color;
clone.style.display="block";
clone.style.backgroundColor=color;
document.getElementById("main").prepend(clone);
setTimeout(function(){
clone.style.left="500px";
},0)
}
setTimeout(function(){move("red")},500);
setTimeout(function(){move("green")},750);
I am expecting the red square to start with left=0px at .5s and move to the right, and then a green square that starts with left=0px at .75s and move to the right. What I am seeing is a red square that starts with left=0px at .5s and moves to the right, and then a green square that starts with left=500px at .75s and does not move.
Edit: This appears to work correctly on Safari on Mac, as well as on Safari and Chrome in iOS. The above suggested behavior only appears on Chrome on Mac.
This is because setTimeout(/**/, 0) does not guarantee that the callback will be executed on a subsequent frame. Which could (depending on browser implementation and computer speed) result in the style being applied on the same frame as the node being inserted into the DOM.
In theory, you should use requestAnimationFrame instead, which is exactly meant for this type of situations.
However, in the fiddle you linked, it only worked if I doubled the requestAnimationFrame which is imperceptible but still undesirable... IDK if it's a fluke of JSFiddle or what...
function move(color){
let clone=document.getElementById("test").cloneNode(true);
clone.id=color;
clone.style.display="block";
clone.style.backgroundColor=color;
document.getElementById("main").prepend(clone);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
clone.style.left="500px";
})
})
}
here's a snippet: I find the same thing in the SO snippet
function move(color) {
let clone = document.getElementById("test").cloneNode(true);
clone.id = color;
clone.style.display = "block";
clone.style.backgroundColor = color;
document.getElementById("main").prepend(clone);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
clone.style.left = "500px";
})
})
}
setTimeout(() => move("red"), 500);
setTimeout(() => move("green"), 750);
#main {
display: block;
width: 100vw;
height: 100vh;
background-color: blue;
}
.test {
position: absolute;
display: none;
width: 100px;
height: 100px;
background-color: red;
transition: left 1s ease;
transform: scale(1);
left: 0px;
}
<div id="main"></div>
<div id="test" class="test"></div>
trying to make a button like this: https://gyazo.com/9afbd559c15bb707a2d1b24ac790cf7a. The problem with the code right now is that it works as it is supposed to on the first time; but after that, instead of going from left to right as intented, it goes from right to left to right.
HTML
<div class="btn-slide block relative mx-auto" style="overflow: hidden; width: 12rem;">
<span class="z-10">View Pricing</span>
<span class="slide-bg block absolute transition" style="background-color: rgba(0,0,0,.1); z-index: -1; top: 0; left:-10rem; width: 10rem; height: 3rem;"></span>
</div>
Javascript
const btns = document.querySelectorAll(".btn-slide");
const slide = document.getElementsByClassName('slide-bg');
btns.forEach(function(btn) {
btn.addEventListener('mouseout', function () {
slide[0].style.transform = 'translateX(230%)';
slide[0].style.transform = 'none';
})
btn.addEventListener('mouseover', function() {
slide[0].style.transform = 'translateX(80%)';
}, true)
})
Unless you have to compute a value in JavaScript (like the height of an element).
Use CSS classes as modifiers (is-hidden, is-folded, is-collapsed, ...).
Using JavaScript, only add/remove/toggle the class
yourElement.addEventListener(
"mouseenter",
function (event)
{
yourElement.classList.remove("is-collapsed");
}
);
yourElement.addEventListener(
"mouseleave",
function (event)
{
yourElement.classList.add("is-collapsed");
}
);
is-collapsed is only an exemple, name it according to your class naming standard.
You're probably going to need a bit more code than what you're showing, as you have two mutually exclusive CSS things you want to do: transition that background across the "button" on mouseenter/mouseout, which is animated, and then reset the background to its start position, which should absolutely not be animated. So you need to not just toggle the background, you also need to toggle whether or not to animation those changes.
function setupAnimation(container) {
const fg = container.querySelector('.label');
const bg = container.querySelector('.slide-bg');
const stop = evt => evt.stopPropagation();
// step one: make label text inert. This is critical.
fg.addEventListener('mouseenter', stop);
fg.addEventListener('mouseout', stop);
// mouse enter: start the slide in animation
container.addEventListener('mouseenter', evt => {
bg.classList.add('animate');
bg.classList.add('slide-in');
});
// mouse out: start the slide-out animation
container.addEventListener('mouseout', evt => {
bg.classList.remove('slide-in');
bg.classList.add('slide-out');
});
// when the slide-out transition is done,
// reset the CSS with animations _turned off_
bg.addEventListener('transitionend', evt => {
if (bg.classList.contains('slide-out')) {
bg.classList.remove('animate');
bg.classList.remove('slide-out');
}
});
}
setupAnimation(document.querySelector('.slide'));
.slide {
position: relative;
overflow: hidden;
width: 12rem;
height: 1.25rem;
cursor: pointer;
border: 1px solid black;
text-align: center;
}
.slide span {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.slide-bg {
background-color: rgba(0,0,0,.1);
transform: translate(-100%, 0);
transition: none;
z-index: 0;
}
.slide-bg.animate {
transition: transform 0.5s ease-in-out;
}
.slide-bg.slide-in {
transform: translate(0%, 0);
}
.slide-bg.slide-out {
transform: translate(100%, 0);
}
<div class="slide">
<span class="label">View Pricing</span>
<span class="slide-bg"></span>
</div>
And thanks to browsers being finicky with rapid succession mouseenter/mouseout events, depending on how fast you move the cursor this may not even be enough: you might very well still need a "step" tracker so that your JS knows which part of your total animation is currently active, and not trigger the mouseout code if, by the time the slide-in transition ends, the cursor is in fact (still) over the top container (or, again).
I advice you use the .on event listener
$('').on("mouseentre","elem",function(){$('').toggleclass('.classname')})
$('').on("mouseleave","elem",function(){$('').toggleclass('.classname')})
Then you can toggle css classes to your element in the function
toggle class adds the css of a class to your jquery selection, you can do it multiple times and have keyframes for animation in the css class
Keyframes are great way to implement animation and are supported on every browers
Im trying to work out script that will change background images every 3 sec using fadeIn, fadeOut, addClass and removeClass.
Is there a better way to do it using setInterval?
$("document").ready(function () {
$("#bg").delay(3000);
$("#bg").fadeOut(300);
$("#bg").removeClass('bg1');
$("#bg").addClass('bg2');
$("#bg").fadeIn(300);
$("#bg").delay(3000);
$("#bg").fadeOut(300);
$("#bg").removeClass('bg2');
$("#bg").addClass('bg1');
$("#bg").fadeIn(300);
});
btw. its not working properly.
HTML:
<div id="bg" class="ShowBG bg1"></div>
CSS:
#bg{
position:absolute;
width:100%;
height:70%;
background-size:cover;
background-position:center;
display:none;
}
.bg1{background-image:url("/img/index/bg1.png");}
.bg2{background-image:url("/img/index/bg2.png");}
Your method should work just fine but it's not the best way to write it: what if your graphic designer suddenly decides to add another background image in the cycle? Your code could become pretty long pretty fast. Here's how I would do it:
var backgroundClasses = ['bg1', 'bg2']; // Store all the background classes defined in your css in an array
var $element = $('.container'); // cache the element we're going to work with
var counter = 0; // this variable will keep increasing to alter classes
setInterval(function() { // an interval
counter++; // increase the counter
$element.fadeOut(500, function() { // fade out the element
$element.removeClass(backgroundClasses.join(' ')). // remove all the classes defined in the array
addClass(backgroundClasses[counter % backgroundClasses.length]). // add a class from the classes array
fadeIn(500); // show the element
});
}, 3000)
.container {
width: 100vw;
height: 100vh;
}
.bg1 {
background-color: red;
}
.bg2 {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container bg1"></div>
The hardest part of the code is this:
$element.addClass(backgroundClasses[counter % backgroundClasses.length])
It basically adds one of the classes stored in the backgroundClasses array. Using the modulo operator (%) on the counter will basically start over every time it has reached the end of the array, counting 0, 1, 0, 1, 0, 1 if you're array is only 2 elements long. If it's 3 elements long it counts 0, 1, 2, 0, 1, 2, ... and so on. Hope that makes sense.
Use callback of fadeOut() method (see complete parameter here) to perform class change when the animation is done. Otherwise the class will swap while the animation is still going.
There is no better way than using setInterval() if you want to do it automatically and continuously.
Here is working example:
$("document").ready(function () {
var bg = $("#bg");
setInterval(function() {
// We fadeOut() the image, and when animation completes we change the class and fadeIn() right after that.
bg.fadeOut(300, function() {
bg.toggleClass('bg1 bg2');
bg.fadeIn(300);
});
}, 1500);
});
#bg {
position:absolute;
width:100%;
height:70%;
background-size:cover;
background-position:center;
}
.bg1 {
background-image: url("https://www.w3schools.com/css/img_fjords.jpg");
}
.bg2 {
background-image: url("https://www.smashingmagazine.com/wp-content/uploads/2015/06/10-dithering-opt.jpg");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bg" class="ShowBG bg1"></div>
Edit
Just noticed OP wants fading so I added a simple CSS transition and opacity properties to both classes and #bg.
Use toggleClass(). Not sure why you used display:none so I removed it. Also I added the dimensions to html and body so your div has something to relate it's percentage lengths with.
Demo
setInterval(function() {
$('#bg').toggleClass('bg1 bg2');
}, 3000);
html,
body {
height: 100%;
width: 100%
}
#bg {
position: absolute;
width: 100%;
height: 70%;
background-size: cover;
background-position: center;
opacity:1;
transition:all 1s;
}
.bg1 {
background-image: url("http://placehold.it/500x250/00f/eee?text=BG1");
opacity:1;
transition:all 1s;
}
.bg2 {
background-image: url("http://placehold.it/500x250/f00/fff?text=BG2");
opacity:1;
transition:all 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bg" class="ShowBG bg1"></div>
So I have images where when you click they expand (via css). However, I also want it so that when you click, the image will be pushed to the top of the page. From what I've heard is that if I use the toggleClass function then I need to have a flag before I initiate the animation, however, I can't seem to get it to function right.
$("img").on("click", function (){
$(this).toggleClass("selected");
if ($("img").hasClass("selected")) {
found = true;
}
var timeout = setTimeout(function () {
$('html,body').animate({
scrollTop: $('.selected').offset().top - 60
}, 100);
}, 5);
});
You should consider using CSS3 transition rather than monitoring using timers. You set transition to transition the top property. Then have the selected class alter the top by toggling it. The change will cause the animation to kick in. See this example:
HTML:
<div class="bar">weee!</div>
CSS:
.bar{
width: 100px;
height: 100px;
background: red;
position: relative;
transition: top 1s ease 0;
top: 100px;
}
.bar.selected{
top : 0px;
}
JS:
$('.bar').on('click',function(){
$(this).toggleClass('selected');
});