Issue
I want css keyframe animations to run 1 time when the bridge element is in the viewport. Intersect observer does this well, but the issue is the animations fire randomly when clicking on other parts of the element that have onClicks to show new text paragraphs.
The point was to call attention to the user that hey! you can click on these parts of the bridge element by showing a delayed box shadow when main element is in view. Maybe there is a better way to show users that an area is clickable, maybe there is a JS rule I am overlooking? Any suggestions appreciated - Thank you!
Example: https://codepen.io/robyngraha/pen/LYaZMVg
function firstBro() {
document.getElementById("first-bro-text").style.display = "block";
document.getElementById("second-bro-text").style.display = "none";
document.getElementById("third-bro-text").style.display = "none";
document.getElementById("first-brother").classList.add("bridge-green");
document.getElementById("second-brother").classList.remove("bridge-green");
document.getElementById("third-brother").classList.remove("bridge-green");
}
function secondBro() {
document.getElementById("first-bro-text").style.display = "none";
document.getElementById("second-bro-text").style.display = "block";
document.getElementById("third-bro-text").style.display = "none";
document.getElementById("first-brother").classList.remove("bridge-green");
document.getElementById("second-brother").classList.add("bridge-green");
document.getElementById("third-brother").classList.remove("bridge-green");
}
function thirdBro() {
document.getElementById("first-bro-text").style.display = "none";
document.getElementById("second-bro-text").style.display = "none";
document.getElementById("third-bro-text").style.display = "block";
document.getElementById("first-brother").classList.remove("bridge-green");
document.getElementById("second-brother").classList.remove("bridge-green");
document.getElementById("third-brother").classList.add("bridge-green");
}
// animation
const bridge = document.querySelector('.bridge');
const secondBroEl = document.querySelector('#second-brother');
const thirdBroEl = document.querySelector('#third-brother');
const observer = new IntersectionObserver(entries => {
secondBroEl.classList.toggle('animation', entries[0].isIntersecting);
thirdBroEl.classList.toggle('animation2', entries[0].isIntersecting);
console.log(entries)
});
observer.observe(bridge);
.scroll-container {
max-width: 480px;
}
/* Bridge CSS */
.bridge-container {
height: 65px;
color: white;
border-radius: 8px;
display: flex;
gap: 2px;
justify-content: space-between;
font-size: 1.3rem;
background: black;
cursor: pointer;
background: linear-gradient(180deg, #28411F 20.31%, #000 100%);
}
/* center div */
#second-brother {
font-size: 1.8rem;
width: 80%;
margin: 0 auto;
position: relative;
display: flex;
align-items: center;
justify-content: center;
border-left: black solid 2px;
border-right: black solid 2px;
}
.animation {
animation-name: highlight;
animation-duration: 1s;
animation-iteration-count: 1;
}
@keyframes highlight {
0% {
box-shadow: none;
}
50% {
box-shadow: 0 0px 10px 10px orange;
}
100% {
box-shadow: none;
}
}
/* center changing text */
#second-brother::after {
content: "2nd";
position: relative;
}
/* outer left div */
#first-brother {
border-radius: 8px 0px 0px 8px;
width: 20%;
height: 65px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
line-height: 20px;
font-size: 1.5rem;
}
/* outer left div text */
#first-brother::after {
content: "1st";
position: relative;
}
/* outer right div */
#third-brother {
border-radius: 0px 8px 8px 0px;
width: 20%;
height: 65px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
line-height: 20px;
font-size: 1.5rem;
}
.animation2 {
animation-name: highlight2;
animation-delay: .5s;
animation-duration: 1s;
animation-iteration-count: 1;
}
@keyframes highlight2 {
0% {
box-shadow: none;
}
50% {
box-shadow: 0 0px 10px 10px orange;
}
100% {
box-shadow: none;
}
}
/* outer right div text */
#third-brother::after {
content: "3rd";
position: relative;
}
@media (max-width: 625px) {
#first-brother-left::after {
margin-left: 12px;
}
}
@media (max-width: 480px) {
#countdown-right,
#countdown-left,
#center-battery {
font-size: 1rem;
}
}
/* Brothers text */
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#first-bro-text {
display: block;
animation: fadeIn .5s;
}
#second-bro-text {
display: none;
animation: fadeIn .5s;
}
#third-bro-text {
display: none;
animation: fadeIn .5s;
}
.bridge-green {
background: linear-gradient(180deg, #4A7938 20.31%, #000 100%);
animation: fadeIn 1s;
}
<div class="scroll-container">
<!-- For Spacing -->
<h1>Scroll down....<br />Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus architecto voluptatem numquam, nihil ullam suscipit, odit placeat labore natus maxime laborum aliquam voluptates animi, dicta facere debitis earum beatae nemo.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Rem totam aut fugiat nostrum animi, sed illum ad minima sint sequi nemo, labore, suscipit et reiciendis fuga corrupti! Provident, tempore ab. Lorem ipsum dolor sit amet consectetur adipisicing
elit. Reiciendis enim ad quos incidunt culpa necessitatibus eveniet aut earum quia, est iste debitis eos quidem facilis facere? Velit temporibus esse adipisci?</h1>
<div class="bridge-container bridge">
<div id="first-brother" class="bridge-green" onclick="firstBro()"></div>
<div id="second-brother" onclick="secondBro()"></div>
<div id="third-brother" onclick="thirdBro()"></div>
<div id="rmg">HELLO</div>
</div>
<div id="first-bro-text">
<p style="color:#225BBE;font-weight:900;">First Brother</p>
<p>So, the oldest brother, who was a combative man, asked for a wand more powerful than any in existence. A wand that must always win battles for its owner. A wand worthy of a wizard who had conquered Death. So, Death had crossed to an Elder Tree on
the banks of the river, fashioned a wand from a branch that had hung there, and gave it to the oldest brother</p>
</div>
<div id="second-bro-text">
<p style="color:#225BBE;font-weight:900;">Second Brother</p>
<p>Then the second brother, who was an arrogant man, decided that he wanted to humiliate Death still further, and asked for the power to recall others from Death. So, Death picked up a stone from the riverbank and gave it to the second brother, and told
him that the stone would have the power to bring back the dead.</p>
</div>
<div id="third-bro-text">
<p style="color:#225BBE;font-weight:900;">Third Brother</p>
<p>Finally, Death turned to the third brother. A humble man, he asked for something that would enable him to go forth from that place without being followed by Death. And so it was that Death reluctantly handed over his own Cloak of Invisibility. </p>
</div>
<div id="click-me">Click Me</div>
<!-- For Spacing -->
<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus architecto voluptatem numquam, nihil ullam suscipit, odit placeat labore natus maxime laborum aliquam voluptates animi, dicta facere debitis earum beatae nemo. Lorem ipsum dolor sit
amet consectetur adipisicing elit. Rem totam aut fugiat nostrum animi, sed illum ad minima sint sequi nemo, labore, suscipit et reiciendis fuga corrupti! Provident, tempore ab. Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis enim
ad quos incidunt culpa necessitatibus eveniet aut earum quia, est iste debitis eos quidem facilis facere? Velit temporibus esse adipisci?</h1>
</div>
I tried setting the animation-iteration-count to 1 in CSS but the animation starts over whenever clicking on 1st, 2nd or 3rd elements that have onClick functions.
I added an onClick function outside of the intersect observation element and the animations do not run, so it must be related to the element with the intersection observer.
I added an element with in the intersection observer without an onclick (Hello text) and the animations do not fire so the issue must be related to elements with an onclick.
Solution
The issue is caused by the animation
and animation2
classes in combination with the bridge-green
class, which all have animations. Whenever your elements come into view, the highlight
animation plays. Then when you click you add the bridge-green
class and overwrite the animation. Removing the bridge-green
class again will trigger the highlight
animation again.
A solution is to expand the Intersection Observer logic only add the animation
class once when the elements into view and then stop observering. This will prevent the animation
class to be added more than once.
Then use the animationend
to remove the animation
class after the animation finishes so that bridge-green
can do it's thing without animation
getting in the way.
function firstBro() {
document.getElementById("first-bro-text").style.display = "block";
document.getElementById("second-bro-text").style.display = "none";
document.getElementById("third-bro-text").style.display = "none";
document.getElementById("first-brother").classList.add("bridge-green");
document.getElementById("second-brother").classList.remove("bridge-green");
document.getElementById("third-brother").classList.remove("bridge-green");
}
function secondBro() {
document.getElementById("first-bro-text").style.display = "none";
document.getElementById("second-bro-text").style.display = "block";
document.getElementById("third-bro-text").style.display = "none";
document.getElementById("first-brother").classList.remove("bridge-green");
document.getElementById("second-brother").classList.add("bridge-green");
document.getElementById("third-brother").classList.remove("bridge-green");
}
function thirdBro() {
document.getElementById("first-bro-text").style.display = "none";
document.getElementById("second-bro-text").style.display = "none";
document.getElementById("third-bro-text").style.display = "block";
document.getElementById("first-brother").classList.remove("bridge-green");
document.getElementById("second-brother").classList.remove("bridge-green");
document.getElementById("third-brother").classList.add("bridge-green");
}
// animation
const bridge = document.querySelector('.bridge');
const secondBroEl = document.querySelector('#second-brother');
const thirdBroEl = document.querySelector('#third-brother');
const observer = new IntersectionObserver((entries, observer) => {
for (const { isIntersecting } of entries) {
if (isIntersecting) {
secondBroEl.classList.add('animation');
thirdBroEl.classList.add('animation');
observer.unobserve(bridge);
}
}
});
observer.observe(bridge);
secondBroEl.addEventListener('animationend', removeAnimationAfterFinish);
thirdBroEl.addEventListener('animationend', removeAnimationAfterFinish);
function removeAnimationAfterFinish(event) {
event.target.classList.remove('animation');
}
.scroll-container {
max-width: 480px;
}
/* Bridge CSS */
.bridge-container {
height: 65px;
color: white;
border-radius: 8px;
display: flex;
gap: 2px;
justify-content: space-between;
font-size: 1.3rem;
background: black;
cursor: pointer;
background: linear-gradient(180deg, #28411F 20.31%, #000 100%);
}
/* center div */
#second-brother {
font-size: 1.8rem;
width: 80%;
margin: 0 auto;
position: relative;
display: flex;
align-items: center;
justify-content: center;
border-left: black solid 2px;
border-right: black solid 2px;
}
#second-brother.animation {
animation-name: highlight;
animation-duration: 1s;
}
@keyframes highlight {
0% {
box-shadow: none;
}
50% {
box-shadow: 0 0px 10px 10px orange;
}
100% {
box-shadow: none;
}
}
/* center changing text */
#second-brother::after {
content: "2nd";
position: relative;
}
/* outer left div */
#first-brother {
border-radius: 8px 0px 0px 8px;
width: 20%;
height: 65px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
line-height: 20px;
font-size: 1.5rem;
}
/* outer left div text */
#first-brother::after {
content: "1st";
position: relative;
}
/* outer right div */
#third-brother {
border-radius: 0px 8px 8px 0px;
width: 20%;
height: 65px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
line-height: 20px;
font-size: 1.5rem;
}
#third-brother.animation {
animation-name: highlight;
animation-delay: .5s;
animation-duration: 1s;
}
/* outer right div text */
#third-brother::after {
content: "3rd";
position: relative;
}
@media (max-width: 625px) {
#first-brother-left::after {
margin-left: 12px;
}
}
@media (max-width: 480px) {
#countdown-right,
#countdown-left,
#center-battery {
font-size: 1rem;
}
}
/* Brothers text */
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#first-bro-text {
display: block;
animation: fadeIn .5s;
}
#second-bro-text {
display: none;
animation: fadeIn .5s;
}
#third-bro-text {
display: none;
animation: fadeIn .5s;
}
.bridge-green {
background: linear-gradient(180deg, #4A7938 20.31%, #000 100%);
animation: fadeIn 1s;
}
<div class="scroll-container">
<!-- For Spacing -->
<h1>Scroll down....<br />Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus architecto voluptatem numquam, nihil ullam suscipit, odit placeat labore natus maxime laborum aliquam voluptates animi, dicta facere debitis earum beatae nemo.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Rem totam aut fugiat nostrum animi, sed illum ad minima sint sequi nemo, labore, suscipit et reiciendis fuga corrupti! Provident, tempore ab. Lorem ipsum dolor sit amet consectetur adipisicing
elit. Reiciendis enim ad quos incidunt culpa necessitatibus eveniet aut earum quia, est iste debitis eos quidem facilis facere? Velit temporibus esse adipisci?</h1>
<div class="bridge-container bridge">
<div id="first-brother" class="bridge-green" onclick="firstBro()"></div>
<div id="second-brother" onclick="secondBro()"></div>
<div id="third-brother" onclick="thirdBro()"></div>
<div id="rmg">HELLO</div>
</div>
<div id="first-bro-text">
<p style="color:#225BBE;font-weight:900;">First Brother</p>
<p>So, the oldest brother, who was a combative man, asked for a wand more powerful than any in existence. A wand that must always win battles for its owner. A wand worthy of a wizard who had conquered Death. So, Death had crossed to an Elder Tree on
the banks of the river, fashioned a wand from a branch that had hung there, and gave it to the oldest brother</p>
</div>
<div id="second-bro-text">
<p style="color:#225BBE;font-weight:900;">Second Brother</p>
<p>Then the second brother, who was an arrogant man, decided that he wanted to humiliate Death still further, and asked for the power to recall others from Death. So, Death picked up a stone from the riverbank and gave it to the second brother, and told
him that the stone would have the power to bring back the dead.</p>
</div>
<div id="third-bro-text">
<p style="color:#225BBE;font-weight:900;">Third Brother</p>
<p>Finally, Death turned to the third brother. A humble man, he asked for something that would enable him to go forth from that place without being followed by Death. And so it was that Death reluctantly handed over his own Cloak of Invisibility. </p>
</div>
<div id="click-me">Click Me</div>
<!-- For Spacing -->
<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus architecto voluptatem numquam, nihil ullam suscipit, odit placeat labore natus maxime laborum aliquam voluptates animi, dicta facere debitis earum beatae nemo. Lorem ipsum dolor sit
amet consectetur adipisicing elit. Rem totam aut fugiat nostrum animi, sed illum ad minima sint sequi nemo, labore, suscipit et reiciendis fuga corrupti! Provident, tempore ab. Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis enim
ad quos incidunt culpa necessitatibus eveniet aut earum quia, est iste debitis eos quidem facilis facere? Velit temporibus esse adipisci?</h1>
</div>
Answered By - Emiel Zuurbier
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.