javascript onclick css fade in each time, not just 1st time - javascript

How can I have text fade in on each click and not just the first time using css transition and JavaScript?
Here is what I have so far
<style>
#data {
transition: .7s;
}
</style>
<h1 id="data"></h1>
click 1
document.getElementById('data').style.opacity = 0;
function go(event){
event.preventDefault();
document.getElementById('data').style.opacity = 1;
document.getElementById('data').innerHTML = 'test';
}
document.getElementById('clicker').addEventListener('click', go);

It can also be done using Element.animate():
const dataElement = document.getElementById('data')
dataElement.style.opacity = 0
function go(event) {
event.preventDefault()
dataElement.animate({
opacity: [0, 1]
}, 700).onfinish = () => dataElement.style.opacity = 1
}
document.getElementById('clicker').addEventListener('click', go)
<h1 class="fade-in" id="data">test</h1>
click 1
EDIT:
In the above snippet onfinish event handler was used to maintain the final opacity value since it was being set back to 0 after the animation ends. But I found that this can also be achieved by setting fill: 'forwards' in the keyframe options:
const dataElement = document.getElementById('data')
dataElement.style.opacity = 0
function go(event) {
event.preventDefault()
dataElement.animate({
opacity: [0, 1]
}, {
duration: 700,
fill: 'forwards'
})
}
document.getElementById('clicker').addEventListener('click', go)
<h1 class="fade-in" id="data">test</h1>
click 1
Also you might want to check browser compatibility before implementing those approaches
And if you want a safer approach you may use css animations:
const data = document.getElementById('data')
data.style.opacity = 0
const clicker = document.getElementById('clicker')
clicker.addEventListener('click', () => {
data.classList.remove('fade-in')
data.offsetWidth // required to trigger a reflow and restart the animation
data.classList.add('fade-in')
})
.fade-in {
animation-name: fadein-animation;
animation-duration: 700ms;
animation-fill-mode: forwards;
}
#keyframes fadein-animation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<h1 id="data">test</h1>
click 1

setTimeout might work for you. On click set it invisible immediately and use setTimeout to have a delay then show it again.
document.getElementById('data').style.opacity = 0;
document.getElementById('data').innerHTML = 'test';
function go(event){
event.preventDefault();
document.getElementById('data').style.opacity = 0;
setTimeout(() => {
document.getElementById('data').style.opacity = 1;
}, 1000);
}
document.getElementById('clicker').addEventListener('click', go);
#data {
transition: .7s;
}
<h1 id="data"></h1>
click 1

<h1 id="data" style="opacity:0"> </h1>
<button type="button" id="clicker">Fade In</button>
<script>
var data = document.getElementById('data');
function fadeIn(){
data.innerHTML = 'Data entered successfully.';
data.animate({opacity:[0,1]},{duration:400,fill:'forwards'});
}
document.getElementById('clicker').addEventListener('click',fadeIn);
</script>

Related

How to play one css anmation before chaging innerHTML and another one after it?

What I need is this: when I click the button I want the old text to slowly disappear, then I want the new one to slowly appear.
But the disappear animation never gets played. Here's my code:
css
.quote-container {
opacity: 0;
}
.appear {
opacity: 0;
animation: fadeIn 1200ms ease-in forwards;
}
.disappear {
opacity: 1;
animation: fadeOut 1200ms ease-in;
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
javascript
let pickARandomQuote = () => {
index = Math.floor(Math.random() * quotes.length);
currentQuote = quotes[index].quote;
currentAuthor = quotes[index].author;
console.log(currentQuote + " \n" + currentAuthor);
let quoteContainer = document.getElementById("quote-container");
quoteContainer.classList.add("disappear");
void quoteContainer.offsetWidth;
quoteContainer.classList.remove("disappear");
void quoteContainer.offsetWidth;
quoteContainer.classList.remove("appear");
document.getElementById("text").innerHTML = currentQuote;
document.getElementById("author").innerHTML = "- " + currentAuthor;
void quoteContainer.offsetWidth;
quoteContainer.classList.add("appear");
return {
quote: currentQuote,
author: currentAuthor
};
};
document.addEventListener("DOMContentLoaded", pickARandomQuote);
let button = document.getElementById("new-quote");
if (button) {
button.addEventListener("click", pickARandomQuote);
}
It seems like the animation on the screen happens independently from the code, so "disappear" animation doesn't have enough time to get played. I tried "setTimeout", but it didn't help though.
You need to wait till disappear animation ends before executing appear.
One of the approaches is setTimeout function.
Example:
let pickARandomQuote = function() {
index = Math.floor(Math.random() * quotes.length);
currentQuote = quotes[index].quote;
currentAuthor = quotes[index].author;
let quoteContainer = document.getElementById("quote-container");
quoteContainer.classList.remove("appear");
quoteContainer.classList.add("disappear");
setTimeout(function () {
document.getElementById("text").innerHTML = currentQuote;
document.getElementById("author").innerHTML = "- " + currentAuthor;
quoteContainer.classList.remove("disappear");
quoteContainer.classList.add("appear");
}, this.duration)
};
document.addEventListener("DOMContentLoaded", { handleEvent: pickARandomQuote, duration: 0 });
let button = document.getElementById("new-quote");
if (button) {
button.addEventListener("click", { handleEvent: pickARandomQuote, duration: 1200 });
}
However, if you can use jQuery, the following Q&A may work for you.
Jquery replacewith fade/animate

Reverse background-position animation without resetting

I created an animation with CSS that changes the background-position of an element over time, to create a sort of scrolling effect with the background.
#keyframes stars-animate {
0% {
background-position: 0 -500px;
}
100% {
background-position: 2000px -500px;
}
}
This works perfectly. However, I also want to start to rewind the animation and create a reverse scrolling event. This is triggered by some irrelevant action.
function triggerReverse(element) {
element.style.animationDirection = 'reverse';
}
However, when I set the animation-direction to reverse, it does work, but not before it flips the entire background.
Am I doing it wrong, or is that the wrong way to do it, and if so, what is the right way?
Edit: I need to be able to reverse the animation while it is playing
UPDATE
The renewed sample code below provide the effect that enables a user to interrupt/pause the animation (during the first iteration) and immediately start to reverse the animation.
Here it is using time to control. Record the elapsed time from the beginning of animation, and calculate how to start the reverse animation. There are 2 iterations defined in css to make a whole loop. Without user intervention, the animation pauses/stops after the first iteration. But if there is, pause the iteration and immediately re-start it with a calculated animation-delay time. This will looks like an immediate reverse, however actually it is a new start.
There is also a trick on how to re-start the animation. Please refer to the code comment.
I searched around but found nobody has mentioned a similar scenario so far, nor a similar solution. Instead of to use time to control, I would like to see other better approaches.
My test also proves that different running environments render slightly different smoothness. Fortunately, here in SO is the best.
Try the solution to see if it can works well in your own scenario.
const span = document.querySelector('span'),
button = document.querySelector('button'),
duration = 10; // animation-during
let startTime;
span.addEventListener('animationstart', () => {
startTime = Date.now();
button.style.visibility = 'visible';
});
span.addEventListener('animationiteration', () => span.style.animationPlayState = 'paused');
span.addEventListener('animationend', () => {
button.style.visibility = 'hidden';
});
button.addEventListener('click', () => {
span.classList.remove('my_anim');
void span.offsetWidth; // safely apply changes
span.classList.add('my_anim');
const elapsed = Date.now() - startTime;
const delay = (elapsed < duration * 1000) ? (elapsed / 1000 - duration * 2) : -duration;
span.style.animationDelay = `${delay}s`;
span.style.animationPlayState = 'running';
});
span.my_anim {
animation: 10s 2 alternate my_move;
}
#keyframes my_move {
from {
margin-left: 0;
}
to {
margin-left: 50%;
}
}
button {
visibility: hidden;
}
<div>
<span class="my_anim">#</span>
</div>
<button>reverse</button>
This example does not use background-position for animation but a plain character.
const span = document.querySelector("span"),
button = document.querySelector("button");
span.addEventListener(
"animationiteration",
function() {
this.classList.add("paused");
button.style.visibility = "visible";
}
);
button.addEventListener(
"click",
function() {
this.style.visibility = "hidden";
span.classList.remove("paused");
}
);
span {
animation: 3s 2 alternate my_move;
}
span.paused {
animation-play-state: paused;
}
#keyframes my_move {
from {
margin-left: 0;
}
to {
margin-left: 50%;
}
}
button {
visibility: hidden;
}
<div>
<span>#</span>
</div>
<button>reverse</button>
NB: Use -webkit- prefix for css animation when necessary.
building off of #themefield's answer above - thanks, #themefield! - this way works the 'best', not perfect. (Sometimes the letter isn't in exactly the right spot when it reverses.)
The approach that worked was
a) reset animation to forward / reverse at the end
b) replace the animation with its opposite on toggling, setting a - start time to try to position it where it was.
Often it works pretty good, sometimes a lot off.
span = document.querySelector('span')
button = document.querySelector('button')
timerElement = document.querySelector('#timerId')
duration = 3; // animation-during
let startTime = Date.now();
toSec = (msec) => msec / 1000
elapsedTimeMsec = () => Date.now() - startTime
elapsedTimeSec = () => toSec(elapsedTimeMsec())
updateTimer = () => timerElement.innerHTML = `${elapsedTimeSec().toPrecision(2)}s`
let intervalHandle;
startTimer = () => {
intervalHandle = window.setInterval(() => {
updateTimer()
}, 500)
}
endTimer = () => {
window.clearInterval(intervalHandle)
intervalHandle = null
}
span.addEventListener('animationstart', () => {
startTime = Date.now();
startTimer()
});
span.addEventListener('animationiteration', () => span.style.animationPlayState = 'paused');
toggleAnimation = (shouldDelay) => {
span.classList.remove('my_anim');
void span.offsetWidth;
span.classList.add('my_anim');
if(span.style.animationDirection !== 'reverse')
span.style.animationDirection = 'reverse';
else
span.style.animationDirection = 'normal';
if(shouldDelay !== null && shouldDelay) {
span.style.animationDelay = `-${elapsedTimeSec()}s`;
} else {
span.style.animationDelay = `0s`;
}
span.style.animationPlayState = 'running';
}
span.addEventListener('animationend', () => {
endTimer()
updateTimer()
toggleAnimation();
});
button.addEventListener('click', () => {
endTimer()
updateTimer()
toggleAnimation(true) // todo pass in delay!
});
span.my_anim {
font-size: 54px;
animation: 3s 1 normal both my_move;
}
#keyframes my_move {
from {
margin-left: 0;
}
to {
margin-left: 50%;
}
}
button {
/*visibility: hidden;*/
}
#timerId {
font-size: 24px;
color: darkturquoise;
position: fixed;
bottom: 0;
left: 50%;
}
<div>
<span class="my_anim">#</span>
</div>
<button>reverse</button>
<span id="timerId"></span>

Javascript function to FadeIn and FadeOut html item - working solution

HTML
<div id="notifyProductName">
Angualar JS
<a href="" ng-click="cart.addItem(product.name,product.price, 1);cart.ShowItem(product.name)">
JavaScript
cart.prototype.ShowItem = function (productName)
{
document.getElementById("notifyProductName").innerHTML = productName";
document.getElementById("notifyProductName").fadeOut(400);
}
I want to display the productName in a html message box and then fade the message box out after a few seconds.
Set a 3 second setTimeout() function and use a CSS class with transition on opacity for the fade.
cart.prototype.ShowItem = function (productName)
{
var el = document.getElementById("notifyProductName");
el.innerHTML = productName;
el.classList.remove('fade');
el.classList.add('show');
var timeout = setTimeout(function() {
el.classList.add('fade');
}, 3000);
}
.notifyProductName {
opacity: 0;
}
.show {
opacity: 1;
}
.fade {
opacity: 0;
transition: opacity .5s;
}
<div id="notifyProductName" class="notifyProductName"></div>

How to fade in and out one by one using css animation and angular?

Consider the following plunker
I have a list of tile that I want to fade in one by one using ng-repeat however the animation fade the entire tile set all together.
Here is my CSS
.fade-up {
animation: fadeUpIn 5s ease-in-out;
}
.fade-in {
animation: fadeUpOut 5s ease-in-out;
}
#keyframes fadeUpIn {
0% {
opacity: 0;
transform: translate3d(0, 100%, 0);
}
100% {
opacity: 1;
transform: none;
}
}
Here is my template:
<div ng-controller="baseController as bCtrl">
<button ng-click="bCtrl.toggleStuff()">
Toggle
</button>
<div ng-repeat="group in bCtrl.groupList">
<div class="tile-style"
ng-repeat="tile in group"
ng-class="{'fade-up': bCtrl.display, 'fade-in': !bCtrl.display}">
{{tile}}
</div>
</div>
</div>
Here is my JS
function toggleStuff() {
self.display = !self.display;
}
Is there a way to fade in the tiles individually?
u want it animate one by one, an easy way is to set them to display one by one, custom ng-animate might cost more
u may need to record timeouts and clear when change happen during loop
for (var i = 0; i < 10; i++) {
(function(a) {
setTimeout(function() {
console.log(a); //0,1,2...
//$scope.list[a].display = false;
//$scope.$apply()
}, i * 1000);
})(i)
}
As the other answer suggests, you could do this adding timeouts.
I have applied the solution in your plunker.
http://plnkr.co/edit/RV6E20IP5VLrMDKkOr4G?p=preview
for (var i = 1; i < 26; i++) {
tile = { val: i , display: false };
if (i % 4 == 0){
tempList.push(tile);
self.groupList.push(tempList);
tempList = []
} else {
tempList.push(tile);
}
(function(index, tile) {
$timeout(function() {
tile.display = true;
}, i * 400);
})(i, tile);
}

Punctuation loading "animation", javascript?

I'm looking for a good way to display some punctuation loading "animation".
What I want is something like this:
This will display at second 1: "Waiting for your input."
This will display at second 2: "Waiting for your input.."
This will display at second 3: "Waiting for your input..."
This will display at second 4: "Waiting for your input...."
This will display at second 5: "Waiting for your input."
This will display at second 6: "Waiting for your input.."
This will display at second 7: "Waiting for your input..."
This will display at second 8: "Waiting for your input...."
And so on.
I started by surrounding the dots in spans and thought I could loop through them with jquery and display one more, one more, one more, then reset to 1. But the code got very clumsy, so I wonder how you would do this?
Pure CSS solution
Demo: jsfiddle.net/feklee/D59P9
HTML:
Waiting<span class="dots"><span>.</span><span>.</span><span>.</span></span> for more.
CSS:
#keyframes dots-1 { from { opacity: 0; } 25% { opacity: 1; } }
#keyframes dots-2 { from { opacity: 0; } 50% { opacity: 1; } }
#keyframes dots-3 { from { opacity: 0; } 75% { opacity: 1; } }
#-webkit-keyframes dots-1 { from { opacity: 0; } 25% { opacity: 1; } }
#-webkit-keyframes dots-2 { from { opacity: 0; } 50% { opacity: 1; } }
#-webkit-keyframes dots-3 { from { opacity: 0; } 75% { opacity: 1; } }
.dots span {
animation: dots-1 1s infinite steps(1);
-webkit-animation: dots-1 1s infinite steps(1);
}
.dots span:first-child + span {
animation-name: dots-2;
-webkit-animation-name: dots-2;
}
.dots span:first-child + span + span {
animation-name: dots-3;
-webkit-animation-name: dots-3;
}
WebKit-only alternative
Advantage: No nested tags. This means that the ellipsis could be put as content
into an ::after pseudo element.
Demo: jsfiddle.net/feklee/vFT7W
HTML:
Waiting<span>...</span> for more.
CSS:
body {
font-family: 'Roboto', sans-serif;
font-size: 50px;
}
#-webkit-keyframes dots {
0% { background-position: 0px; }
100% { background-position: 50px; }
}
span {
background: linear-gradient(to right, white 50%, black 50%);
color: transparent;
-webkit-background-clip: text;
-webkit-animation: dots 1s infinite steps(4);
padding-right: 40px;
margin-right: -40px;
}
The trick to making a string of dots is to make a sparse Array and then join all the elements with the desired character.
var count = 0;
setInterval(function(){
count++;
var dots = new Array(count % 10).join('.');
document.getElementById('loadingtext').innerHTML = "Waiting for your input." + dots;
}, 1000);
Here is a Live demo.
This can be very easy:
HTML
<span class="dots"></span>
JQuery
setInterval(function() {
var th = $('.dots');
if(th.text().length < 5){
th.text(th.text()+".");
}else{
th.text("");
}
}, 500);
Demo
Now sure how the code got out of hand, you could just do:
setInterval(function () {
var span = $("#text-loader").children("span:eq(0)");
var ellipsis = span.html();
ellipsis = ellipsis + ".";
if (ellipsis.length > 5) {
ellipsis = ".";
}
span.html(ellipsis);
}, 1000);
<div id="text-loader">
This will display at second 1: "Waiting for your input<span>.</span>
</div>
And as for the 1, you can swap that out with the number of periods.
try this function: i'v put an example here http://jsfiddle.net/XFd39/
var i=0;
function f() {
if(i<=4)
$('#a').append(".");
i++;
if(i==4){
$('#a').html("");
i=0;
}
setTimeout(f,500);
}
f();
Here is a pretty simple variant: http://jsfiddle.net/psycketom/FusdC/
Read the comments below to understand what everything is doing there.
var span = $('.dots'); // take the element where you have the maximum count of dots
var text = span.text(); // cahce it's text value
// we set up a function here, so we can loop it all the time
var loading = function()
{
$({
count : 1 // we start at one dot
}).animate({
count : text.length // together forming all of it
}, {
duration : 1000, // make the animation complete in one second
step : function() {
span.text( text.substring(0, Math.round(this.count)) ); // on each step, change the text accordingly to current iteration
},
complete : function() {
loading(); // once complete, start all over again
}
});
};
loading(); // start it up for the first time
Here you also gain the advantage of using easing if you wish, easily changing total duration and bunch of other benefits in case you're good with jQuery.
Dude, unless you want to display this animation forever you will need a way to stop the animation, or?
Don't even think about global variables, this is JavaScript and it's got closures for that :)
<p>please wait<span id="wait"></span></p>
<input type="submit" id="start" value="start">
<input type="submit" id="stop" value="stop">
<script type="text/javascript">
$(document).ready(function() {
var animator = function($el) {
var dotCount = 1;
var started = true;
return {
"start" : function step() {
dotCount = (dotCount + 1) % 10;
$el.text(new Array(dotCount).join('.'));
if (started) {
setTimeout(step, 100);
}
},
"stop" : function() {
started = false;
}
}
};
var animatedWait = animator($("#wait"));
$("#start").click(animatedWait.start);
$("#stop").click(animatedWait.stop);
});
</script>
Here is a modified version that will turn off the dots after a time.
var count = 0;
var dots = setInterval(function(){
count++;
document.getElementById('loadingtext').innerHTML = "Waiting for your input." + new Array(count % 5).join('.');
if (count == 10){ // show two iterations of the array.
clearInterval(dots); // stop the dots.
}
}, 100); // sets the speed
http://jsfiddle.net/Ty4gt/331/

Categories