Uncall/reversing an 'onclick' function when another 'onclick' function is active - javascript

I have a problem where when I click an element with an 'onclick' function, it works just as normal but when I click another element with a different 'onclick' function, the first 'onclick' function will remain. Instead, I want to be able able to reverse(?) the first function so that it is no longer active.
These are h1 tags that are meant to act as nav and when I click on one of them they change their styles.
Here is my code:
function aboutActive() {
var about = document.querySelector('.about');
about.classList.toggle('about-active');
}
function contactActive() {
var contact = document.querySelector('.contact');
contact.classList.toggle('contact-active');
}
function discoverActive() {
var discover = document.querySelector('.discover');
discover.classList.toggle('discover-active');
}
function signUpActive() {
var signUp = document.querySelector('.sign-up');
signUp.classList.toggle('signUp-active');
}
.about {
position: absolute;
left: 70.8%;
top: 5%;
transition: transform 0.8s ease-in;
transition: 0.8s ease-in;
}
.contact {
position: absolute;
left: 56%;
top: 24%;
transition: transform 0.8s ease-in;
transition: 0.8s ease-in;
}
.discover {
position: absolute;
left: 52.7%;
top: 43%;
transition: transform 0.8s ease-in;
transition: 0.8s ease-in;
}
.sign-up {
width: 100%;
position: absolute;
left: 62.6%;
top: 63%;
transition: transform 0.8s ease-in;
transition: 0.8s ease-in;
}
/* New styles applied by JS */
.about-active {
transform: translateX(-30%);
color: #ffffff;
}
.contact-active {
transform: translateX(-22%);
color: #ffffff;
}
.discover-active {
transform: translateX(-24%);
color: #ffffff;
}
.signUp-active {
transform: translateX(-14.2%);
color: #ffffff;
}
<h1 class="about" onmouseover=cursorEnlargeLarge() onmouseout=cursorNormal() onclick="aboutActive()">ABOUT</h1>
<h1 class="contact" onmouseover=cursorEnlargeLarge() onmouseout=cursorNormal() onclick="contactActive()">CONTACT</h1>
<h1 class="discover" onmouseover=cursorEnlargeLarge() onmouseout=cursorNormal() onclick="discoverActive()">DISCOVER</h1>
<h1 class="sign-up" onmouseover=cursorEnlargeLarge() onmouseout=cursorNormal() onclick="signUpActive()">SIGN UP</h1>
These are the functions that toggle through the styles when clicked on.
To reiterate, when I click on one of the h1 element, it does its 'onclick' function normally but if I want to click on a second h1 element, the second element's function will activate but the first will stay. How would I change it so that any previous functions will be reversed/uncalled?

function undo(ele){
let cl = ele.classList;
ele.classList.remove(cl[cl.length - 1], cl[cl.length - 2]);
}
function aboutActive() {
let about = document.querySelector('.about');
let current = document.querySelector(".current");
if(current) undo(current);
about.classList.toggle('about-active');
about.classList.add("current");
}
function contactActive() {
let contact = document.querySelector('.contact');
let current = document.querySelector(".current");
if(current) undo(current);
contact.classList.toggle('contact-active');
contact.classList.add("current");
}
I'm just adding 'current' class to active element.
If 'current' already exists then remove last 2 classes of the 'current' element.
It's not the best one but it works.
(well it's my first answer on stackoverflow so don't hate on me please)

From what I understand, you want when you click on other function uncall function you clicked before
var about = document.querySelector('.about');
var contact = document.querySelector('.contact');
function aboutActive() {
about.classList.toggle('about-active', true);
}
function contactActive() {
about.classList.toggle('about-active', false);
contact.classList.toggle('contact-active');
}

Add a Reset function before click of any H1 which will reset all the h1 tag click
function aboutActive() {
resetAll();
var about = document.querySelector('.about');
about.classList.toggle('about-active');
}
function contactActive() {
resetAll();
var contact = document.querySelector('.contact');
contact.classList.toggle('contact-active');
}
function discoverActive() {
resetAll();
var discover = document.querySelector('.discover');
discover.classList.toggle('discover-active');
}
function signUpActive() {
resetAll();
var signUp = document.querySelector('.sign-up');
signUp.classList.toggle('signUp-active');
}
function resetAll() {
var getheading = document.getElementsByTagName("H1");
[].forEach.call(getheading, function(el) {
var classes = el.className.split(" ").filter(c => !c.endsWith("-active"));
el.className = classes.join(" ").trim();
})
}
.about-active {
transform: translateX(-30%);
color: #ffffff;
}
.contact-active {
transform: translateX(-22%);
color: #ffffff;
}
.discover-active {
transform: translateX(-24%);
color: #ffffff;
}
.signUp-active {
transform: translateX(-14.2%);
color: #ffffff;
}
<h1 class="about" onclick="aboutActive()">ABOUT</h1>
<h1 class="contact" onclick="contactActive()">CONTACT</h1>
<h1 class="discover" onclick="discoverActive()">DISCOVER</h1>
<h1 class="sign-up" onclick="signUpActive()">SIGN UP</h1>

So what I ended up doing is just removing the active class when I call the function:
function aboutActive() {
var about = document.querySelector('.about');
var contact = document.querySelector('.contact');
var discover = document.querySelector('.discover');
var signUp = document.querySelector('.sign-up');
about.classList.toggle('about-active');
contact.classList.remove('contact-active');
discover.classList.remove('discover-active');
signUp.classList.remove('signUp-active');
}
I have to this for every h1 element so it isn't the best way but it works.

Related

CSS transition creating problem in JavaScript? [duplicate]

Consider such div:
<div id="someid"></div>
And it's style:
#someid {
transition: background-color 10s ease;
background-color: #FF0000;
height: 100px;
width: 100px;
}
#someid:hover {
background-color: #FFFFFF;
}
I want to have a possibility to detect state (currently animating or not) of #someid via JS and/or end animation if that's possible. I've tried a thing from this answer:
document.querySelector("#someid").style.transition = "none";
but it didn't work for currently animating element.
The point is I need to detect whether element is animating now and if so, wait for animation to end or end it immediately, otherwise do nothing
I've already found transitionend event, but using it I can't detect whether element is animating at the moment.
You can listen to transition event and remove it on demand:
const el = document.getElementById('transition');
let isAnimating = false;
el.addEventListener('transitionstart', function() {
isAnimating = true;
});
el.addEventListener('transitionend', () => {
isAnimating = false;
});
el.addEventListener('transitioncancel', () => {
isAnimating = false;
});
function removeTransition(checkIfRunning) {
if (checkIfRunning && !isAnimating) {
return;
}
el.style.transition = "none";
}
#transition {
width: 100px;
height: 100px;
background: rgba(255, 0, 0, 1);
transition-property: transform background;
transition-duration: 2s;
transition-delay: 1s;
}
#transition:hover {
transform: rotate(90deg);
background: rgba(255, 0, 0, 0);
}
<div id="transition">Hello World</div>
<br />
<button onclick="removeTransition(false)">Remove Transition</button>
<br />
<br />
<button onclick="removeTransition(true)">Remove Transition on if running</button>

How to retrigger function (translate animation )

My animation function only runs once. I've tried removing and adding classes, as well as running a animationend function to create a retrigger. But still no luck. Any vanilla JS ideas?
CSS:
#element {
width: 10px;
height: 10px;
animation: "";
#keyframes movedown {
100% {
transform: translateY(10px);
}
}
JS:
btn_button.onclick = () => {
element.style.animation = "movedown 10s";
};
HTML:
<div id="element"></div>
You can use setTimeout to set element.style.animation to "". Then, you can add animation name again upon button click.
let btn = document.querySelector("#btn");
btn.onclick = () => {
element.style.animation = "movedown 2s";
setTimeout(() => element.style.animation = "", 2000)
};
#element {
width: 10px;
height: 10px;
background-color: red;
}
#keyframes movedown {
100% {
transform: translateY(50px);
}
}
<div id="element"></div>
<button id="btn">Trigger</button>

Counter JS issue , doesn't toggle class every 1s

Counter doesn't work properly . I would like to get result when counterFun function toggle .active class and show number in every 1s. For now class is toggled but shows every 2nd number and it doesn't looks like it happen every 1sek
let clickNumber = 0;
const time = 1000;
const h1 = document.querySelector('h1');
function counterFun() {
clickNumber++;
h1.textContent = clickNumber;
h1.classList.toggle('active');
console.log(clickNumber);
}
setInterval(counterFun, time);
.regular {
opacity: 0;
}
.active {
font-size: 100px;
opacity: 1;
transition: .4s;
}
HTML
<div>
<h1 class="regular active">0</h1>
</div>
Your problem is you are using .toggle which doesn't toggle again until the second iteration. So basically you end up with:
0 - toggle
1 - off
2 - toggle
3 - off
Your options are to either use .remove and .add for the class or add a second .toggle.
Example with remove:
let clickNumber = 0;
const time = 1000;
const h1 = document.querySelector('h1');
function counterFun() {
h1.classList.remove('active');
clickNumber++;
h1.textContent = clickNumber;
h1.classList.add('active');
console.log(clickNumber);
}
setInterval(counterFun, time);
.regular {
opacity: 0;
}
.active {
font-size: 100px;
opacity: 1;
transition: .4s;
}
HTML
<div>
<h1 class="regular active">0</h1>
</div>
Example with Toggle:
let clickNumber = 0;
const time = 1000;
const h1 = document.querySelector('h1');
function counterFun() {
h1.classList.toggle('active');
clickNumber++;
h1.textContent = clickNumber;
h1.classList.toggle('active');
console.log(clickNumber);
}
setInterval(counterFun, time);
.regular {
opacity: 0;
}
.active {
font-size: 100px;
opacity: 1;
transition: .4s;
}
HTML
<div>
<h1 class="regular active">0</h1>
</div>
Because you need to remove and add the class at the same time, you can use this code
let clickNumber = 0;
const time = 1000;
const h1 = document.querySelector('h1');
function counterFun() {
$("#demo").addClass("active").delay(800).queue(function(next){
$(this).removeClass("active");
clickNumber++;
h1.textContent = clickNumber;
next();
});
console.log(clickNumber);
}
setInterval(counterFun, time);
.regular {
opacity: 0;
}
.active {
font-size: 100px;
opacity: 1;
transition-delay: .4s;
transition: .4s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<h1 class="regular active" id="demo">0</h1>
</div>
PS: you need jquery

How I can animate this slideshow(by changing its src)?

Okay okay, so before marking this post as repeated. Let me explain to you:
I made a slideshow in javascript(Vue) and it works by changing its src in an object every time I press a button(next)
It works and all but the problem is that it doesn't get animated no matter what I do, I made a transition on them, set timeout function on it...etc and nothing even the smallest worked.
I could have made another idea which works by the position absolute but I don't want to do that because it will take a loot of time and it will be extremely buggy as position absolute ruins it. So any help on this please?
<template>
<main>
<div id="slideshow">
<figure id="pics">
<img id="slidepic" v-bind:src="pictures[count].src">
<figcaption>{{pictures[count].alt}}</figcaption>
</figure>
</div>
<p>{{count+1}}/{{pictures.length}}</p>
<div id="controls">
<div #click="move(-1)">Previous</div>
<div #click="move(1)">Next</div>
</div>
</main>
Javascript:
methods: {
move: function(num) {
let slideimg = document.querySelector("#slidepic");
slideimg.classList.add("fadeOut");
this.count += num;
if (this.count < 0) {
this.count = this.pictures.length - 1;
} else if (this.count >= this.pictures.length) {
this.count = 0;
}
setTimeout(function() {
slideimg.src = this.pictures[1].src;
}, 1000);
}
}
CSS:
#pics {
opacity: 0.5s;
transition: 0.5s;
}
#pics.fadeOut {
opacity: 1;
}
I didn't include the object(that is in data object, something in Vue) because it would be useless in this situation.
First off all it's transition: <property-name> 0.5s linear; and not transition: 0.5s;. See the transition documentation.
There is no animation for changing the src of an image (see list of animatable css properties).
To do something like this, you can stack all your images into one element and then use css animations and the transform property to create a carousel
var next = document.getElementById('next');
var prev = document.getElementById('prev');
var slideshow = document.getElementById('slideshow');
next.onclick = function() {
var lastChild = slideshow.children[slideshow.children.length - 1];
var firstChild = slideshow.children[0];
var activeEle = document.querySelector('.item.active');
var nextEle = document.querySelector('.item.next');
var prevEle = document.querySelector('.item.prev');
activeEle.classList.remove('active');
activeEle.classList.add('prev');
nextEle.classList.add('active');
nextEle.classList.remove('next');
prevEle.classList.remove('prev');
if (nextEle.nextElementSibling) {
nextEle.nextElementSibling.classList.add('next');
} else {
firstChild.classList.add('next');
}
};
prev.onclick = function() {
var lastChild = slideshow.children[slideshow.children.length - 1];
var activeEle = document.querySelector('.item.active');
var nextEle = document.querySelector('.item.next');
var prevEle = document.querySelector('.item.prev');
// Move the .active class to the previous element
activeEle.classList.remove('active');
activeEle.classList.add('next');
prevEle.classList.add('active');
prevEle.classList.remove('prev');
nextEle.classList.remove('next');
if (prevEle.nextElementSibling) {
prevEle.nextElementSibling.classList.add('prev');
} else {
lastChild.classList.add('prev');
}
};
#slideshow {
width: 100px;
height: 100px;
overflow: hidden;
position: relative;
}
.item {
width: 100%;
height: 100%;
color: white;
background-color: blue;
position: absolute;
/*display: none;*/
top: 0;
left: 0;
z-index: -100;
transition: translateX(-100%);
transition: transform .5s ease-in-out;
opacity: 0;
}
.active {
opacity: 1;
display: block;
z-index: 1;
transform: translateX(0);
}
.next {
transform: translateX(200%);
z-index: 1;
}
.prev {
transform: translateX(-100%);
opacity: 1;
z-index: 1;
}
<div id="slideshow">
<div class="item active">1</div>
<div class="item next">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item prev">7</div>
</div>
<button type="button" id="prev">Prev</button><button type="button" id="next">Next</button>
As you mention you want to build a slideshow on Vue JS, and because jQuery on top of Vue is not recommended, I suggest that you try Vueper Slides, available on NPM. Unless it is for a learning purpose.
I have created two solutions.
First of all. You've a typo.
#pics {
opacity: 0.5s; // <--- remove "s"
transition: 0.5s; // <--- and forgot the property-name (all, opacity ...)
}
#pics.fadeOut {
opacity: 1;
}
I commented all lines I've changed.
Solution
<template>
<main>
<div id="slideshow">
<!--
I recommend to you ref inestad of querySelector.
https://vuejs.org/v2/api/#ref
I've used the v-bind shorthand.
-->
<figure id="pics1" ref="pics1">
<img id="slidepic" :src="pictures[count].src">
<figcaption>{{pictures[count].alt}}</figcaption>
</figure>
<!--
VueJS build-in transition element.
You have to add a key attribute to detect that the content has changed.
I recommend to use this instead of your solution.
It's easier to implement, no class add/remove struggle, its a part of vue, you can add hooks etc.
https://vuejs.org/v2/guide/transitions.html
-->
<transition tag="figure" name="fade" ref="pics2">
<figure id="pics2" :key="`figure-${count}`">
<img :src="pictures[count].src">
<figcaption>{{pictures[count].alt}}</figcaption>
</figure>
</transition>
</div>
<p>{{count+1}}/{{pictures.length}}</p>
<div id="controls">
<div #click="move(-1)">Previous</div>
<div #click="move(1)">Next</div>
</div>
</main>
</template>
<script>
export default {
name: 'teams',
data() {
return {
count: 0,
pictures: [
{
src: 'https://picsum.photos/200/300',
alt: 'test'
},
{
src: 'https://picsum.photos/200/400',
alt: 'test2'
}
]
};
},
methods: {
// instead of move: function(num) {} you can also write move() {}
move(num) {
this.count += num;
if (this.count < 0) {
this.count = this.pictures.length - 1;
} else if (this.count >= this.pictures.length) {
this.count = 0;
}
}
},
// Watch "count" changes and add or remove classes
// you can also add this to your "move" method
watch: {
count() {
// access the reference
const element = this.$refs.pics1;
element.classList.add('fadeOut');
element.classList.remove('fadeIn');
setTimeout(() => {
element.classList.remove('fadeOut');
element.classList.add('fadeIn');
}, 500); // same duration as css transition
}
}
};
</script>
<style scoped lang="scss">
#pics1 {
opacity: 1;
transition: opacity 0.5s;
}
#pics1.fadeIn {
opacity: 1;
}
#pics1.fadeOut {
opacity: 0;
}
// All classes for <transition>
// There are all automatically used by vue
.fade-enter-active {
transition: opacity 0.5s;
}
.fade-leave {
display: none;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>

requestAnimationFrame is not triggering the function supplied to it

I implemented a infinite loop animation using setInterval. I now like to change the implementation to requestAnimationFrame() so that I will have performance which I am after. For some reasons, requestAnimationFrame() does not call the function supplied to it.
My code looks like this;
var index = 0;
var $btn = $('.btn');
function btnBlinkRun() {
if (index < 2) {
index = index + 1;
} else {
index = 0;
}
$('#ani--scaleinout').removeAttr('id');
$($btn[index]).attr('id', 'ani--scaleinout');
window.requestAnimationFrame(btnBlinkRun);
}
btnBlinkRun();
.btn{
width: 30px;
height: 30px;
background: blue;
border-radius: 100%;
margin-bottom: 10px;
}
#ani--scaleinout {
animation: zoominout 1s ease-in;
}
#keyframes zoominout {
50% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div class="btn" id="ani--scaleinout"></div>
<div class="btn"></div>
<div class="btn"></div>
</div>
It looks like what's going on is you are firing requestAnimationFrame multiple times per second. Your css animation has a duration of 1s. But you are removing the attribute every x ms.
It is triggering, it's just happening so fast you can't see it. To demonstrate change your call to window.requestAnimationFrame to use a setTimeout and you'll notice the animation:
setTimeout(function() {
window.requestAnimationFrame(btnBlinkRun);
}, 1000);
Not saying this is a preferred solution, but explaining why this is happening.
It executes alright. But it does not do what you want it to, i presume.
Animation frame fires on every single rending frame (e.g. 60fps) and not on CSS animation keyframes.
The animationend event is your friend here.
var index = 0;
var buttons = document.querySelectorAll('.btn');
function btnBlinkRun() {
if (index < 2) {
index = index + 1;
} else {
index = 0;
}
const element = document.querySelector('#ani--scaleinout');
element.id = null;
buttons[index].id = 'ani--scaleinout';
buttons[index].addEventListener("animationend", btnBlinkRun, { once: true });
}
btnBlinkRun();
.btn{
width: 30px;
height: 30px;
background: blue;
border-radius: 100%;
margin-bottom: 10px;
}
#ani--scaleinout {
animation: zoominout 1s ease-in;
}
#keyframes zoominout {
50% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
<div>
<div class="btn" id="ani--scaleinout"></div>
<div class="btn"></div>
<div class="btn"></div>
</div>

Categories