Halting CSS animation with ::after selector in JavaScript - javascript

I have a JavaScript function that types out, letter by letter, a message. However, where the current character to be typed is located, I have a blinking css animation. What I need is to stop this animation and make it disappear.
I am using a css with #id::after to put the animation after the text in question. The animation works fine, I need a way to set content: '█'; to content: ''; via JavaScript.
(function type_p(){
let msg = 'This is a message to type out! Spooky!';
let element = document.getElementById('typehere');
typeOut(msg, '', 0, element);
}());
function typeOut(text, letter, index, element){
letter = text.slice(0, ++index);
element.textContent = letter;
if (letter.length == text.length){
stop();
}
setTimeout(typeOut, 100, text, letter, index, element);
}
#typehere {
position: relative;
}
#typehere::after {
content: '█';
position: absolute;
animation: blink 1.5s infinite;
/* animation-iteration-count: 2; */
}
#keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
51% {
opacity: 0;
}
100% {
opacity: 0;
}
}
<p id="typehere">Here</P>
I am aware of CSS animation-iteration-count: however this will stop the animation but it will still be visible (motionless). How do I remove this?

I would just add a class to your element and change the content based on class.
(function type_p(){
let msg = 'This is a message to type out! Spooky!';
let element = document.getElementById('typehere');
typeOut(msg, '', 0, element);
}());
function typeOut(text, letter, index, element){
letter = text.slice(0, ++index);
element.textContent = letter;
if (letter.length == text.length){
element.classList.add('stop');
stop();
}
setTimeout(typeOut, 100, text, letter, index, element);
}
#typehere {
position: relative;
}
#typehere::after {
content: '█';
position: absolute;
animation: blink 1.5s infinite;
/* animation-iteration-count: 2; */
}
#typehere.stop::after {
content: '';
}
#keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
51% {
opacity: 0;
}
100% {
opacity: 0;
}
}
<p id="typehere">Here</P>

This is similar to the other answer, but I find it easier/cleaner than adding multiple classes to something that will no longer be visible.. You can add the animation styling as a class, and then remove that class when you no longer want it to animate.
Change to class in css:
.typehereclass::after {
content: '█';
position: absolute;
animation: blink 1.5s infinite;
}
Add the class to your element in html:
<p id="typehere" class="typehereclass">Here</P>
And then when you want to stop the blinking in JS:
element.classList.remove('typehereclass')

Related

Continuously changing the width of an element with setInterval( )

So I want to change the width of a span element inside a h1 element that have the textContent changed every 4s with setInterval().I want to do that to add a transition to the width property to fill the space between the h1 and the next word that span will have smoothly.
I tried to get the width of the span with getBoundingClientRect() but that didn't work and when I set the width it remains the width of the first element and it's not changed dynamically as I would want.
Here is the code:
const changingSpan = document.querySelector('.changing-span');
let array = ['best', 'tastiest', 'freshest'];
let count = 0;
setInterval(function() {
count++;
let elementWidth = changingSpan.getBoundingClientRect().width.toString();
if(count === array.length) count = 0;
changingSpan.classList.add('animation-span');
changingSpan.textContent = array[count];
changingSpan.style.width = `${elementWidth}px`;
changingSpan.addEventListener('animationend', function() {
changingSpan.classList.remove('animation-span');
})
}, 4000);
.changing-span {
display: inline-block;
color: #c82929;
transition: width .2s ease;
}
.animation-span {
animation: moveDown .8s ease;
}
#keyframes moveDown {
0% {opacity: 0;
transform: translateY(-50%);
}
100% {
transform: translateY(0);
}
}
<h1 class="first-heading">The <span class="changing-span">best</span> burgers in town.</h1>
consider an animation using max-width:
const changingSpan = document.querySelector('.changing-span');
let array = ['best', 'tastiest', 'freshest'];
let count = 0;
setInterval(function() {
count++;
if (count === array.length) count = 0;
changingSpan.classList.add('animation-span');
changingSpan.textContent = array[count];
changingSpan.addEventListener('animationend', function() {
changingSpan.classList.remove('animation-span');
})
}, 4000);
.changing-span {
display: inline-block;
color: #c82929;
}
.animation-span {
animation: moveDown .8s ease;
}
#keyframes moveDown {
0% {
opacity: 0;
transform: translateY(-50%);
max-width:0;
}
100% {
transform: translateY(0);
max-width:200px; /* a big value here */
}
}
<h1 class="first-heading">The <span class="changing-span">best</span> burgers in town.</h1>
Your code is very good.
But you need to remove 2 rows like below, then you code works well as you want
let elementWidth = changingSpan.getBoundingClientRect().width.toString();
changingSpan.style.width = ${elementWidth}px;
So the answer was posted by Sean above(Thank you). I will put here the code snippet for this in case somebody will search for something similar and will want to do the same thing:
const changingSpan = document.querySelector('.changing-span');
const changingSpanWrapper = document.querySelector('.changing-span-wrapper');
let array = ['best', 'tastiest', 'freshest'];
let count = 0;
changingSpanWrapper.style.width = `${changingSpan.getBoundingClientRect().width.toString()}px`;
setInterval(function() {
count++;
if(count === array.length) count = 0;
changingSpan.classList.add('animation-span');
changingSpan.textContent = array[count];
changingSpan.addEventListener('animationstart', function() {
let elementWidth = changingSpan.getBoundingClientRect().width.toString();
let elementHeight = changingSpan.getBoundingClientRect().height.toString();
changingSpanWrapper.style.width = `${elementWidth}px`;
changingSpanWrapper.style.height = `${elementHeight}px`;
})
changingSpan.addEventListener('animationend', function() {
changingSpan.classList.remove('animation-span');
})
}, 4000);
.changing-span-wrapper {
display: inline-block;
transition: width .2s ease, height .2s ease;
}
.changing-span {
display: inline-block;
color: #c82929;
}
.animation-span {
animation: moveDown 1s ease;
}
#keyframes moveDown {
0% {opacity: 0;
transform: translateY(-50%);
}
100% {
transform: translateY(0);
}
}
<h1 class="first-heading">The <span class="changing-span-wrapper"><span class="changing-span">best</span></span> burgers in town.</h1>

SetTimeout lags between calls in Firefox

I'm trying to optimize my code so that I can cycle through images on a splash page quickly and effectively. I've got it looking really smooth on chrome and safari but when I view the splash page on mobile and firefox it bugs out big time
A demo can be found at http://theotherchrisrock.com
I would love to hear your input on how to fix this. Here is the relevant code:
var i = 0
var j = 0
var l = $('.se-pre-con > div').length - 2;
var $pre_con = $('.se-pre-con');
var $di_sum = $('.splash-image:last-child');
var $img_array = [];
for (t = 0; t < l; t++) {
$img_array[t] = $('.splash-image-' + t);
}
function flashanimation() {
if (i < l) {
$img_array[i].addClass('flash');
i++;
flashloop();
} else if (j != 1) {
$di_sum.addClass('di-some');
j = 1
flashloop();
} else {
$pre_con.addClass('nun');
}
}
function flashloop() {
setTimeout(function() {
flashanimation();
}, 300)
}
$(".blinker").removeClass("blinker");
flashloop();
Basically, the goal is to make the image appear for 150ms and then disappear for 150ms and then next image appears and so on finally ending with just a black div. Right now I'm adding a class to each div which triggers this keyframe animation ~
.splash-image.flash {
-webkit-animation:flash 0.15s linear;
animation:flash 0.15s linear;
-webkit-animation-delay:0.15s;
animation-delay:0.15s;
display:block;
opacity:0;
}
#-webkit-keyframes flash {
0% {
opacity:0;
}
1% {
opacity: 1;
}
100% {
opacity:1;
}
}
#keyframes flash {
0% {
opacity:0;
}
1% {
opacity: 1;
}
100% {
opacity:1;
}
}
I would love to hear your input. Thank u for reviewing my question

Changing Text with Fade Javascript Multiple times

I am trying to create my own website, where it has an initial quote in the center:
"Welcome to my website", and when you hover over the menu options, the quote should change.
For example: if I hover over About it could say: "This is my About me Page". (The example is obviously simplified).
HTML Snippet:
About
Contact
Home
<div>
<p>
Welcome to my Website!
</p>
</div>
CSS Snippet:
.fade {
animation: fadein 2s;
}
#keyframes fadein {
from {
opacity:1;
}
50% {
opacity: 0;
}
to {
opacity:1;
}
}
JS Snippet
let myP = document.querySelector('p');
let titleWords = document.querySelector('a.titleAbout');
titleWords.addEventListener('mouseenter', function(){
myP.classList.add("fade");
setTimeout(function(){
myP.innerHTML = "This is my about Me page";
}, 1000);
setTimeout(function(){
text.classList.toggle("fade");
}, 2000);
});
titleWords = document.querySelector('a.titleContact');
titleWords.addEventListener('mouseenter', function(){
myP.classList.add("fade");
setTimeout(function(){
myP.innerHTML = "This is my Contact page";
}, 1000);
setTimeout(function(){
text.classList.toggle("fade");
}, 2000);
});
However - it only does the fade properly the first time I hover over a menu item, the other times it changes the innerHTML, but doesn't fade in and out.
Why exactly is this happening an how can I fix it?
you have not defined the text variable in your javascript. I think you mean myP.classList.toggle("fade");
let myP = document.querySelector('p');
let titleWords = document.querySelector('a.titleAbout');
titleWords.addEventListener('mouseenter', function(){
myP.classList.add("fade");
setTimeout(function(){
myP.innerHTML = "This is my about Me page";
}, 1000);
setTimeout(function(){
myP.classList.toggle("fade");
}, 2000);
});
titleWords = document.querySelector('a.titleContact');
titleWords.addEventListener('mouseenter', function(){
myP.classList.add("fade");
setTimeout(function(){
myP.innerHTML = "This is my Contact page";
}, 1000);
setTimeout(function(){
myP.classList.toggle("fade");
}, 2000);
});
.fade {
animation: fadein 2s;
}
#keyframes fadein {
from {
opacity:1;
}
50% {
opacity: 0;
}
to {
opacity:1;
}
}
About
Contact
Home
<div>
<p>
Welcome to my Website!
</p>
</div>
You can do it shorter and without setTimeout:
let myP = document.querySelector('p');
function show(textToShow){
myP.innerHTML = textToShow;
myP.classList.add("fade");
}
function hide(){
myP.classList.remove("fade");
}
.fade {
animation: fadein 3s;
}
#keyframes fadein {
from {
opacity:1;
}
50% {
opacity: 0;
}
to {
opacity:1;
}
}
About
Contact
Home
<div>
<p>
Welcome to my Website!
</p>
</div>
I have refactored your code to a more generic version, so you can have as many links as you want and do not need to repeat your code. It takes a title attribute from the a tag as text. Maybe this behaves more like you intended, you can play around uncommenting that timeout.
const myPContainer = document.getElementById('pContainer');
const titleWords = document.querySelectorAll('a');
let i;
function attachHandlers(i, elem) {
let timeoutHandle;
const pElem = document.createElement('p');
pElem.innerHTML = elem.getAttribute('title');
myPContainer.appendChild(pElem);
elem.addEventListener('mouseenter', function(){
timeoutHandle = null;
pElem.classList.add("fadein");
});
elem.addEventListener('mouseleave', function(){
// timeoutHandle = setTimeout(() => {
pElem.classList.remove("fadein");
// }, 2000);
});
}
for (i = 0; i < titleWords.length; ++i) {
attachHandlers(i, titleWords[i]);
}
#pContainer {
position: relative;
height: 20px;
}
#pContainer p {
position: absolute;
display: block;
opacity: 0;
}
.fadein {
animation: fadein 2s;
display: block;
}
#keyframes fadein {
from {
opacity: 0;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}
About
Contact
Home
<div id="pContainer"></div>

How stay final state of animation CSS and remove animation property?

I want to stay with final properties of css animation, but remove animation property itself, exmeple:
#keyframes('foo')
{
0% { opacity: 0; }
to { opacity: 1; }
}
at the begining :
.my_element
{
opacity: 0;
}
I apply animation by Javascript, so I got :
.my_element
{
opacity: 0;
}
element.style
{
animation: foo 1s forwards; #with opacity: 1; at the end.
}
And now I need clean up :
.my_element
{
opacity: 0;
}
element.style
{
opacity: 1;
}
How can I do that?
This does exactly what you're asking. On animationEnd, the cleanUp function is passed an object containing the class to remove after the animation, the property to replace on the element, and the property value to replace on the element. Check out the DOM before and after clicking button the and you'll see that this works properly.
var el = document.querySelector(".my_element"),
cleanUp = function(data) {
el.classList.remove(data.remove);
el.style[data.prop] = data.val;
},
startAnimation = function() {
el.classList.add("animate");
};
el.addEventListener("animationend", function() {
var cleanup_data = {
remove: "animate",
prop: "opacity",
val: 1
};
cleanUp(cleanup_data);
});
#keyframes foo {
from { opacity: 0; }
to { opacity: 1; }
}
.animate {
animation: foo 3s forwards;
}
.my_element {
opacity: 0;
}
<div class="my_element">some text</div>
<button onclick="startAnimation()">Start Animation</button>

Cannot get setTimeout working

I am working on a simple css animation for webkit. A number of elements will slide in and then unfold.
My idea is to keep to have a single css animation and trigger it by adding the slideIn class with jquery.
.slideIn {
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
-webkit-animation-name: slideIn;
clip: rect(0,125px,315px,0);
}
#-webkit-keyframes slideIn {
0% { -webkit-transform: translateX(0); }
50% { -webkit-transform: translateX(-700px); clip: rect(0,125px,315px,0); }
100% { -webkit-transform: translateX(-700px); clip: rect(0,550px,315px,0); }
}
As I mentioned, there are more than one item on the page and I want them to slide in one by one. So I need to add the slideIn class one by one, with somtehing like:
var pref = "animated";
var animClass = "slideIn";
var eNumber = 5;
var interval = 10000;
animateMultiple(pref, eNumber, interval, animClass);
function animateMultiple(pref, eNumber, interval, animClass) {
var i = 0;
function addAnimClass() {
i++;
$('#' + pref + i).addClass(animClass);
if(i < eNumber){
setTimeout(addAnimClass(), interval);
}
}
addAnimClass ();
}
Yet, this ends in all the element sliding in alltogether.
Why does not the interval have any effect?
setTimeout(addAnimClass(), interval);
You are (immediately) calling addAnimClass and passing its return value (undefined) to setTimeout.
Remove the ().

Categories

Resources