Adding style to CSS classes in Javascript - javascript

I am trying to add an animation-play-state style to a CSS class on button click through Javascript but I am not sure where am doing it wrong.
here is what I did.
function play(){
sky.style.animation-play-state: play;
sun.style.animation-play-state: play;
}
function pause(){
sky.style.animation-play-state: pause;
sun.style.animation-play-state: pause;
}
playButton.onclick = play;
pauseButton.onclick = pause;
But am getting this error in my console
Uncaught SyntaxError: Unexpected token ':'

You have to use camelCase to refer to CSS style properties (this is because "-" means minus in JavaScript, so the properties are all mapped to camelCase).
You also need to assign the states with "=" in JavaScript. You're using ":" which is CSS syntax.
The animation play states are also strings, so you have to wrap them in quotes.
And finally, the states you want are actually "running" and "paused", not "play" and "pause".
Here's a working snippet of what you're trying to do:
let sun = document.querySelector("#sun");
let sky = document.querySelector("#sky");
let playButton = document.querySelector("#playButton");
let pauseButton = document.querySelector("#pauseButton");
function play(){
sky.style.animationPlayState = "running";
sun.style.animationPlayState = "running";
}
function pause(){
sky.style.animationPlayState = "paused";
sun.style.animationPlayState = "paused";
}
playButton.onclick = play;
pauseButton.onclick = pause;
#sun, #sky {
padding: 10px;
transform-origin: center;
animation-name: rotate;
animation-duration: 8s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-play-state: paused;
}
#keyframes rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
<div style="display: flex">
<div id="sun">Sun</div>
<div id="sky">Sky</div>
</div>
<div>
<button id="playButton">Play</button>
<button id="pauseButton">Pause</button>
</div>

Use running and paused for play and pause:
function Playpause() {
var pauseState = getId.style.animationPlayState === 'paused';
var runState = getId.style.animationPlayState === 'running';
varName.style.animationPlayState = runState ? 'paused':'running';
}

Instead of using two functions for play and pause you can instead create one and use javascript toggle method to toggle the class. And you can define animation state in the class.

Related

Simple appearing animation

Just make a very very simple animation:
p {
animation: appear 1s linear 1;
font-family:monospace;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any helps!</p>
It works, but I am just thinking is it possible to make the first letter appear first, after the first letter appearing, then the second, and finally the last letter (now the whole content of p will appear at the same time)
Does CSS have any selectors to select every characters in the element and is it possible to achieve the effect use CSS only?
I only know first-letter and last-letter selector in css, but not sure if css has selector for every letters
If not only with CSS, I would be also Ok with JS solution?
Appreciate for any helps provided~
Here's a vanilla JS approach. First we extract the text, empty the element, then rebuild it one character at a time.
let p = document.querySelector('.anim');
// store value
let t = p.innerText.split(''),
counter = 0
p.innerHTML = '';
let inter = setInterval(() => {
if (counter == t.length) clearInterval(inter)
else p.insertAdjacentHTML('beforeend', `<span>${t[counter++]}</span>`);
}, 100)
p.anim span {
animation: appear 1s linear 1;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p class='anim'>Thank you for any helps!</p>
If a JS-based solution is acceptable, then you can easily achieve this using a combination of DOM manipulation (wrapping each non-space character in a <span> element), and then sequentially toggling a class that runs the animation.
The sequential part can be achieved by simply looping through the generated <span> element, and then awaiting for a promise to be resolved.
In your question it is not clear how you want to be a delay to be calculated: I simply assume you want an arbitrary/customizable delay. In this case, a simple promise-based sleep function can get the job done:
function sleep(ms = 0) {
return new Promise(r => setTimeout(r, ms));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await sleep(100);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>
If you want to wait for each character before fading in the next, then change the await function into something that hooks into the animationend event, although I am inclined to believe this is not what you intend. Just putting it out here for the sake of completeness:
function onAnimationEnd(el) {
return new Promise(r => el.addEventListener('animationend', () => r()));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await onAnimationEnd(span);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>

How to replay this CSS animation each time a user clicks?

I've created a button that displays the text "Copied!" each time the user clicks on the "Copy" button. The way I've accomplished this is by assigning the animation to the relevant tag and by also using setTimeout to "reset" the animation. This way, when a user clicks the button a second or third time, the animation plays again.
However, I'm certain this is not an ideal solution because if a user clicks before setTimeout finishes, the button 'breaks' until 2500ms have passed. After that, it works again.
How can I refactor the CSS/JS so that the "Copied!" message displays each time the button is clicked? (Without being limited/delayed by a timeout)
const copyURL = () => {
const copyDiv = document.querySelector(".copyAlert")
copyDiv.textContent = "Copied!";
copyDiv.style.animationName = "disappear";
copyDiv.style.animationDuration = "2.5s";
setTimeout(function(){
copyDiv.style.animationName = "none";
}, 2400);
};
.copyAlert {
opacity: 0;
}
#keyframes disappear {
30% {
opacity: 0;
}
60% {
opacity: 1;
}
100% {
opacity: 0;
}
}
<button onclick="copyURL()">Copy</button>
<span class="copyAlert"></span>
1) Use animationend event
2) Use animate class for animation
3) Don't use JS to insert "Copied!" text, just write it in your html
const copyURL = () => {
const copyDiv = document.querySelector('.copyAlert:not(.animate)')
if(copyDiv) {
copyDiv.classList.add('animate');
copyDiv.addEventListener('animationend', () => copyDiv.classList.remove('animate') );
}
};
.animate { animation: disappear 2.5s linear; }
.copyAlert { opacity: 0; }
#keyframes disappear {
30% { opacity: 0; }
60% { opacity: 1; }
100% { opacity: 0; }
}
<button onclick="copyURL()">Copy</button>
<span class="copyAlert">Copied!</span>
If possible try with setTimeOut() function setTimeOut
to change your text from Copy to Copied! [How to change the button text on click for a short duration only using javascript?

Make a Header disappear and reappear after waiting 2 seconds

I ANSWERED DOWN BELOW
I want to make the header of my document once scrolled play an animation to fade and then use style.display to make it unusable. Here is the code:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
</script>
I don't know how to make this though so that it will wait two seconds before running the document.getElementById('header').style.display = "none".
I have in the <style> tag to do the fade out animation with the .opacity, it is just the .display that I want to make wait the 2 seconds of animation.
Also I have no idea how to use JQuery or other documents' code so I need it to be purely in HTML, JavaScript or CSS. Thanks. (I'm a noob)
If what you need is to run after 2 seconds
For that you can make use of setInterval or setTimeout depending on your needs.
setTimeout(function(){ alert("This is after 3 sec"); }, 3000);
setInterval : Runs after every specified time interval.
setTimeout : Runs only once after waiting specified time.
W3CSchool Doc
If what you need is to wait till the DOM is loaded.
For that you will have to check the event DOMContentLoaded.
whatever the code you have it should be within this.
document.addEventListener("DOMContentLoaded", function (event) {
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
}
I hope this solved your problem.
The best way to approach this for performance and maintainability is to let css handle the animation and only use javascript to capture the event and add a class.
Also, you need a way to make sure the function bound to the scroll event only fires once.
Finally, if you want to destroy the element, you can use javascript to listen for the 'animationend' event (the name is browser-dependent), which is captured by the browser, and fire your function to destroy the element at that time (instead of having a fixed 2 second delay as you are trying to do).
Here's a codepen demo of the implementation I just described: http://codepen.io/anon/pen/NdWGqO
Here's the relevant js and css:
//js
var head = document.getElementById('header');
var hasScrolled = false;
function onScroll() { // a function should only start with a capital letter if it is a constructor
if (!hasScrolled) {
head.className += " fade-out";
window.removeEventListener("scroll",onScroll); // remove this so it only fires once
}
hasScrolled = true; // this prevents the class from being added multiple times
}
function destroyElement() {
head.className += " hidden"; // the 'hidden' class will give the element 'display : none'
}
function captureEvents () {
window.addEventListener("scroll",onScroll);
// capture the animation end event, there are better ways to do this to support all browsers FYI
head.addEventListener("animationend", destroyElement);
head.addEventListener("webkitAnimationEnd", destroyElement);
head.addEventListener("oanimationend", destroyElement);
head.addEventListener("MSAnimationEnd", destroyElement);
}
document.addEventListener("DOMContentLoaded", captureEvents);
//css
.hidden {
display: none;
}
.fade-out {
animation-name: fadeOut;
animation-duration: 4s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
-webkit-animation-name: fadeOut;
-webkit-animation-duration: 4s;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
}
#keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
#-webkit-keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
I figured it out... here is the code for js:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header');
var timeOut;
var displayNone = 'head.style.display = "none"'
if (window.pageYOffset < 1)
{
clearTimeout(timeOut);
}
if (window.pageYOffset > 1)
{
timeOut = setTimeout(function(){displayNone}, 2000);
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
stopcount();
}
}
window.addEventListener("scroll",Scroll);
Then in css:
#header
{
height: 100px;
width: 100%;
position: fixed;
background:radial-gradient(circle,#777,#666);
transition:all 0.2s;
}
And finally in HTML:
<header id="header"></header> /* header info goes in here */
idk why but when I set the display none part to a variable it fixed it.

Triggering CSS3 Keyframes with javascript multiple times

I am triggering CSS3 Keyframes with javascript but its working for with first call after that any call to that function doesn't animate my div.
Here the Javascript code
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s"
//shareTools.style.animationPlayState = "running";
}
Sample of the issue (Click red box to preview)
var box = document.getElementById("box");
function animateBox(){
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
JSFIDDLE
I want it to animate everytime i call this function.
You can use well known hack: destroy and create element to reset animation.
var box = document.getElementById("box");
function animateBox(){
//destroy and create hack
document.body.removeChild(box);
document.body.appendChild(box);
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
In case someone is still interested by this there is another trick that works:
Changing the dataset value to something it has never been, and then use a css matching on that data.
I used this technique when animating the collapsing/uncollapsing of a menu, which of course can happen multiple times. Here's how I did it:
#menu[data-closed^="1"]{
animation:menu_closing;
}
#menu[data-closed^="0"]{
animation:menu_opening;
}
So the animation is based on the first character of the dataset (1 or 0).
Then in the click event that wants to close/open the menu:
var closed = menu_ele.dataset.closed // closed right now
? parseInt( menu_ele.dataset.closed.substr(0,1) )
: 0; // assuming menu is initialized open
var counter = menu_ele.dataset.closed // nb of times the menu was closed or open
? parseInt( menu_ele.dataset.closed.substr(1) )
: 0;
menu_ele.dataset.closed = ''+(1-closed)+(counter+1);
This way the "closed" dataset variable changes like this at every click:
11 (closing for the first time)
02 (reopening for the first time)
13
04
15
06
...
The first digit indicates whether it is currently closed, while all the rest is a counter to make the value new every time.
Think about your code - after first call it does nothing, becouse it already changed animation property of that element.
According to this CSS-Tricks article:
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s";
shareTools.style.animationPlayState = "paused";
shareTools.style.animationPlayState = "running";
}
add this code in the body section after the element for which the animation is being played-
<script>
document.getElementById('shareTools').addEventListener("animationend", function () {
this.removeAttribute("style");
})
</script>
or if you dont want to remove style attribute ,because you have other css than animation then create a class and add class dynimacally and remove it as above code.
You can reset the animation by just removing the animation property from the styles of the element after the animation has complete - removing the element is unnecessary. In my example, I set the duration in JS, but you could just as easily add an animationend hook to keep it simpler.
JSFiddle
var duration = 1000;
document.getElementById('box').addEventListener('click', function onClick(ev) {
var el = this;
el.style.animation = 'box ' + (duration / 1000 + 's') + ' ease-in-out';
setTimeout(function() {
el.style.animation = ''
}, duration);
});
#box {
background: red;
width: 100px;
height: 100px;
}
#keyframes box {
50% {
width: 300px;
}
}
<div id='box'><div>

Fading in and out with ES6 and CSS3

So I have a function that I'm trying to create the loops through an array to update a div's innerHTML with JavaScript. I was hoping to set the opacity to 0 and then 1 between setting the new data each time, without using jQuery's fadeIn() and fadeOut().
Here is what I have so far. I think I'm very close, but not sure what I'm doing that's slightly off.
Thanks!
slide(index, tweets, element) {
let self = this;
element.innerHTML = data[index].text;
element.style.opacity = 1;
setTimeout(() => {
index++;
element.style.opacity = 0;
setTimeout(self.slide(index, data, element), 2000);
}, 5000);
}
EDIT
I forgot to mention I'm banking on CSS3 for animation by adding a class to my div that changes with this:
transition: opacity 2s ease-in-out;
I don't know how the code you provided relates to the problem at hand, but here's a simple demo on how to fade out, change the text and then fade back in.
You should be able to expand on this for your needs.
var d = document.querySelector("div");
window.addEventListener("load", function() {
d.classList.add("hidden");
});
var i = 0;
d.addEventListener("transitionend", function() {
if (this.classList.contains("hidden")) {
i++;
this.innerHTML = "SUCCESS! ---> " + i;
}
this.classList.toggle("hidden");
});
div {
opacity: 1;
transition: opacity 2s;
}
div.hidden {
opacity: 0;
}
<div>LOADING...</div>
It just adds the hidden class to trigger the fade out and it binds a transitionend handler to change the text and remove the class for the fade in.

Categories