Adding Flag to javascript (hasClass) - javascript

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');
});

Related

setTimeout and display none with a fader timer- How to do it chronologically?

I am sitting with a project in need of an overlay which fades out when hovered upon and goes to display: none (not visibility: hidden, it does need to be display: none).
The setup is a big confusing, but I will try to explain it:
The overlay comes up when I hover a menu point under my mega menu. When I move the cursor to the overlay it should naturally dissapear and the menu close.
This works very well with this code:
var element = document.getElementById("overlayed");
function mouseOver() {
element.classList.add("mystyle");
setTimeout(function() {
element.classList.remove("mystyle");
}, 500);
}
push {
position: absolute;
top: 50%;
}
.overlayerstwo {
position: fixed;
width: 100%;
top: 30%;
left: 0;
right: 0;
bottom: 0;
background: #111;
opacity: 0.5;
z-index: 2;
display: block;
visibility: visible;
}
.mystyle {
display: none;
animation-name: fadeOut;
animation-duration: .5s;
}
#keyframes fadeOut {
0% {
opacity: .5
}
100% {
opacity: 0;
}
}
.mystyler {
display: none;
}
<h1>Here is something. Overlay comes back when hovering me!</h1>
<div class="overlayerstwo" id="overlayed" onmouseover="mouseOver()"></div>
<div class="push">
<p>Here is an item being overlayed</p>
</div>
With this setup the overlay dissapears right away. I am trying to merge it with the fadeOut keyframe animation before it goes black. I have tried different tactics, like adding a second timeout event but all it does is loop through and end up showing the overlay permanently after.
So the order I want to achieve is as follows:
Add a class that fires the keyframe animation fadeOut for .5 sec
Remove keyframe animation class
Add display: block class
Remove display: block class (essentially resetting it, so you can get the overlay up again by hovering its triggerpoint)
So my question is, how do I get all of these to fire every time I hover over the overlay?
One of the things I tried was this:
var element = document.getElementById("overlayed");
element.classList.add("mystyle");
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.remove("mystyle");
}, 500);
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.add("mystyletwo");
}, 500);
setTimeout(function(){
var element = document.getElementById("overlayed");
element.classList.remove("mystyletwo");
}, 510);
With the css
.mystyle{
animation-name: fadeOut;
animation-duration: .5s;
}
.mystyletwo{
display: block;
}
Which did not work. I hope someone can help me figure out how to get it to work!
if the timeline will be like this: visible -> hover -> animation -> opacity to 0 -> display: none
using CSS with JS logic:
element.addEventListener("mouseover", function() {
element.style.opacity = "0";
element.style.transition = "all 0.3s";
// when finish the animation then call display none
setTimeout(function() {
element.style.display = "none";
}, 300); // put the same number (milliseconds) of duration of transition (or more, not less)
});
using this method you don't need to complex your code...
the trick really is because we use element.style
that is only put the CSS, but technically...
if there is a transition Javascript don't know it,
so it will run the setTimeout() directly after adding styles,
so now CSS will do the animation but javascript will quietly continue the code (which in our case, says that after 300 seconds add display: none;)

How to detect changes made by CSS3 animation or web animations API?

How can I detect changes made by CSS3 animations or web animations (Element.animate)??
(Sorry for my bad English! this is my first question in Stackoverflow)
I know about MutationObserver, it responds only if I change the inline style or if I use requestAnimationFrame (since I change the inline style using it). But if I use CSS3 animations or Web animations, MutationObserver doesn't respond since they don't change the inline style.
See this... There are two divs here... div1, div2. div1's position will change when div2's position changes. But this happens only if I use requestAnimationFrame as I said before.
My question is how can I do this for css3 animations and web animations (Element.animate)?
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
/***** Add mutation observer to detect change *****/
const mutation = new MutationObserver(mutations => {
div1.style.left = div2.style.left;
});
mutation.observe(div2, {
attributes: true
});
/***** Animation with css *****/
function cssAnimation() {
div2.style.animation = 'anim 1.5s linear';
}
/***** Animation with web animations *****/
function webAnimation() {
div2.animate({
left: [0, '500px']
}, {
duration: 1500,
easing: 'linear'
});
}
/*****Animation with requestAnimationFrame ******/
// Current left position of div2
const left = 0;
function requestAnimation() {
// Increase left position 5px per keyframe
div2.style.left = `${(left += 5)}px`;
// Increase left position until it reaches to 500px
if (left < 500) {
requestAnimationFrame(requestAnimation);
}
}
function clearAnimations() {
left = 0;
div2.style.left = 0;
div2.style.animation = 'unset';
}
#keyframes anim {
from {
left: 0;
}
to {
left: 500px;
}
}
#div1 {
background: orange;
width: 100px;
height: 100px;
position: absolute;
top: 200px;
}
#div2 {
background: lightgreen;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
}
<div id="buttons">
<h3>Animate with...</h3>
<button onclick='cssAnimation()'>Css3</button>
<button onclick="requestAnimation()">request animation frame</button>
<button onclick="webAnimation()">web animations api</button>
<button id="clear" onclick="clearAnimations()">Clear</button>
</div>
<div id="div1">
Div1
</div>
<div id="div2">
div2
</div>
Both CSS Animations and Web Animations are based on the principle that you delegate the playback of the animation to the browser. That allows the browser to run the animation on a separate thread or process when possible, so that it runs smoothly. Updating style from JavaScript on each frame is best avoided where possible.
In your example, can you simply run the animation on both elements at the same time? Web Animations, at least, allows synchronizing the animations.
When the remainder of the Web Animations API is shipped, it will be much easier to duplicate animations from one element and apply them to another but for now you would need to call animate twice.
As others have pointed out, it is possible to observe significant moments in the playback of animations (when they start, finish, repeat etc.) but there is no event that runs on each frame. If you want to perform and action on each frame you need to use requestAnimationFrame.
If you pass div2 to getComputedStyle in requestAnimationFrame you will get the animated style for that frame which you can then apply to div1. (That is, reading div2.style.left will only give you the style specified via the style attribute but getComputedStyle(div2).left will give you the animated style including style changes from CSS animations and Web Animations). But, again, that will lead to poor performance and the two animations will not necessarily be synchronized since the CSS animation or Web animation may run on a different thread or process.
You can use requestAnimationFrame and window.getComputedStyle() to get current animated styles during the animation, note, included fill:"forward" at Element.animate() call
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
/***** Add mutation observer to detect change *****/
var mutation = new MutationObserver(function(mutations) {
div1.style.left = div2.style.left;
});
mutation.observe(div2, {
attributes: true
});
/***** Animation with css *****/
function cssAnimation() {
div2.style.animation = "anim 1.5s linear forwards";
let animationFrame;
function getCurrentStyles() {
console.log(window.getComputedStyle(div2).left);
animationFrame = requestAnimationFrame(getCurrentStyles)
}
getCurrentStyles();
div2.addEventListener("animationend", () => cancelAnimationFrame(animationFrame));
}
/***** Animation with web animations *****/
function webAnimation() {
let animationFrame;
function getCurrentStyles() {
console.log(window.getComputedStyle(div2).left);
animationFrame = requestAnimationFrame(getCurrentStyles)
}
getCurrentStyles();
div2.animate({
left: [0, "500px"]
}, {
duration: 1500,
fill: "forwards",
easing: "linear"
}).onfinish = function() {
cancelAnimationFrame(animationFrame);
console.log(window.getComputedStyle(div2).left);
};
}
/*****Animation with requestAnimationFrame ******/
//Current left position of div2
var left = 0;
function requestAnimation() {
//Increase left position 5px per keyframe
div2.style.left = `${(left += 5)}px`;
console.log(window.getComputedStyle(div2).left);
//Increase left position until it reaches to 500px
if (left < 500) {
requestAnimationFrame(requestAnimation);
}
}
function clearAnimations() {
left = 0;
div2.style.left = 0;
div2.style.animation = "unset";
}
#keyframes anim {
from {
left: 0;
}
to {
left: 500px;
}
}
#div1 {
background: orange;
width: 100px;
height: 100px;
position: absolute;
top: 200px;
}
#div2 {
background: lightgreen;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
}
<div id="buttons">
<h3>Animate with...</h3>
<button onclick='cssAnimation()'>Css3</button>
<button onclick="requestAnimation()">request animation frame</button>
<button onclick="webAnimation()">web animations api</button>
<button id="clear" onclick="clearAnimations()">Clear</button>
</div>
<div id="div1">
Div1
</div>
<div id="div2">
div2
</div>
For css animations, you have the animationstart and animationend events that could help you with what you are trying to achieve. However, there is no animationchange or animationupdate event, and it is this way, as far as I know, by design. Without events during the animation happening, it is possible to reach full hardware acceleration, with the interpolation computations done directly in the GPU. So, be aware that while you might be able to mimic what an animationchange event would do via animationstart, animationend and requestAnimationFrame, this is probably going to involve a performance penalty.
You're over complicating things. But to listen for animation changes you can listen on these events instead of the mutation observer.
animationstart //will fire has soon as the animation starts
animationiteration //will fire if the same animation is looped can be infinity or more then 2
animationend // will fire when the animations have ended
also please use translate instead of animating the left property.

Repeat animation by clicking button

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!

How to make two elements appear and disappear on a top of each other in an infinite loop

I trying to make header one disappear, then header two appear and disappear and header one appear in a loop. The problem with my code is, that header two appears only for very short amount of time and empty screen follows for at least 2-3 seconds before header one appears.
JS:
var h1 = $('.header_one');
var h2 = $('.header_two');
setInterval(function(){
h1.fadeOut(1000);
h2.fadeIn(1000);
h2.fadeOut(2000, function() { h1.fadeIn(); });
}, 4000);
HTML:
<h1 class="header_one"> HEADER ONE </h1>
<h2 class="header_two"> HEADER TWO </h2>
CSS:
h1, h2 {
z-index: 10;
position: relative;
}
h2 {display: none;}
There is also a problem: when header two disappears, my two links underneath h1 and h2, that are set to position: relative; move to the top of the page.
Here's one way to do it: jsfiddle
$("#box1").hide();
function animate() {
$("#box2").fadeOut(1000, function() {
$("#box1").fadeIn(1000, function() {
$("#box1").fadeOut(1000, function() {
$("#box2").fadeIn(1000, animate);
});
});
});
}
animate();
div {
position: absolute;
left: 20px;
top: 20px;
background: red;
width: 50px;
height: 50px;
}
div:nth-child(2) {
left: 30px;
top: 30px;
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box1"></div>
<div id="box2"></div>
The reason why your links are moving up when the headers disappear is because the fadeIn and fadeOut are manipulating the display property which removes it from the flow of the document. Only the visibility property would retain the element's position and reserve the space for it.
Assuming that your 2 headers look the same in terms of styling, why not use just one and change the text inside it while animating the color/transparency?
But if you really want to fire them in sequence given your code then you need to do what you did on the last line, where one is a callback when the other finishes; however, this won't fix your links moving up problem.
This is a really nasty/bad way of doing it, but it works...
setInterval(function(){
$('h1').fadeOut(function(){
$('h2').fadeIn();
});
}, 4000);
setInterval(function(){
$('h2').fadeOut(function(){
$('h1').fadeIn();
});
}, 8000);
this is my option to do a loop with jquery animate:
https://jsfiddle.net/vbw8jLko/
var h1 = $('.header_one');
var h2 = $('.header_two');
function loop(){
console.log('llop');
$( ".header_one" ).stop().css('opacity', 0).show().animate({opacity:1}, 700).delay(700).animate({
opacity: 0
}, 1000, function(){
$(this).hide();
$( ".header_two" ).stop().css('opacity', 0).show().delay(100).animate({
opacity: 1
}, 1000).delay(700).animate({opacity:0}, 1000, function(){
$(this).hide(); loop()});
});
}
loop();
Expect like it :)

Append divs from bottom with scroll - Chat application example

I'm looking to append divs from the bottom. At a certain point, the vertical scroll should kick in so you can view divs that were appended earlier on. I'm trying to replicate a typical chat application and how messages come from the bottom. Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
Before I put the code, I'll explain a couple of workarounds I've tried thus far. The pen currently has the container absolutely positioned with a bottom of 0. The problem, which is a pain, is that once the height goes beyond the height of the viewport, it won't scroll. This is the problem with the absolute positioning workaround.
Another workaround I've tried is doing a height of 100vh and display of flex with justify-content flex-end so the columns start at the bottom. The problem with this is that the scroll will always start from the top. I believe the solution is a scroll function that I've created to scroll to the bottom every time a new div is added. Would this be the best method? The key here is that I want to be able to scroll up to the older divs but have the newer divs start from the bottom. Think of a typical chat application like slack or messages or similar.
HTML
<button onclick="myFunction()">Hey here's a box</button>
<div id="container">
</div>
CSS
body {
margin: 0;
width: 100%;
}
button {
position: fixed;
z-index: 10;
}
#container {
position: absolute;
bottom: 0;
width: 100%;
}
#box {
width: 100%;
background: tomato;
opacity: 0;
height: 100px;
transition: .2s;
}
#box:last-child {
opacity: 1;
height: 0;
animation: .2s height linear forwards;
}
#keyframes height {
to {
height: 100px;
}
}
#box:nth-last-child(2) {
opacity: .8;
}
#box:nth-last-child(3) {
opacity: .6;
}
#box:nth-last-child(4) {
opacity: .4;
}
#box:nth-last-child(5) {
opacity: .2;
}
JS
function myFunction() {
var box = document.createElement("div");
box.setAttribute("id", "box");
var container = document.getElementById('container');
container.appendChild(box);
// window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}
Is there a better solution than the function I've created to scroll to the bottom? Much appreciated.
Ok, so after messing around with some JS, I figured it out. I love when that happens...
Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
I created a setInterval function for scrolling to the bottom.
var myVar = setInterval(function(){
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}, .1);
However, since this interval runs every .1 seconds, I need to kill it in order to scroll around the divs above (like old chat messages), but I want the animation (of the new div coming in) to finish. So, I created a setTimeout function to kill the setInterval function at 200 ms.
setTimeout(function(){
clearInterval(myVar);
}, 200);

Categories

Resources