I have mobile navigation bar that toggles on clicking the burger and everything works fine. The navigational links have animation that needs to be triggered every time when there is no animation on them (achieved with JavaScript, code will be shown below) and everything works fine until the moment I click on one navigational link. So the navigation bar closes, and the animation is triggered on closing, thus not on opening and the next time I click on the burger in order to open the menu the animation does not play. BUT, the thing that confuses me the most is that after clicking on one of the navigational links, on the next menu-opening the animation is here and everything works fine (the bug comes after clicking on one of the navigational links).
How to make the animation play every time on opening the menu?
HTML code:
<nav>
<div class="logo">
Portfolio
</div>
<ul class="nav-links">
<li>Home</li>
<li>About Me</li>
<li>Skills</li>
<li>Contact</li>
</ul>
<div class="burger">
<div class="line1"></div>
<div class="line2"></div>
<div class="line3"></div>
</div>
</nav>
CSS code:
// if nav is active translate him to 0 (since he is translated)
.nav-active {
transform: translateX(0%);
}
.nav-links {
position: absolute;
right: 0;
height: 93vh;
top: 7vh;
background-color: white;
box-shadow: 0 5px 50px white;
display: flex;
flex-direction: column;
justify-content: center;
width: 40%;
transform: translateX(110%);
transition: transform 0.5s ease-in;
}
.nav-links li {
opacity: 0;
list-style: none;
display: flex;
align-items: center;
flex-direction: column;
}
.nav-links a {
margin-left: 0;
color: #3e689d;
font-size: 2.2em;
margin-bottom: 50px;
transition: 0.5s ease;
padding: 30px 30px;
}
.burger div {
width: 40px;
height: 5px;
background-color: white;
margin: 5px;
transition: all 0.5s ease;
}
//animation for burger
.toggle .line1 {
transform: rotate(-45deg) translate(-6.5px, 7.5px);
}
.toggle .line2 {
background-color: black;
transform: translateX(-100px) scale(0);
}
.toggle .line3 {
transform: rotate(45deg) translate(-6.5px,-7.5px);
}
// animation for links
#keyframes navLinkFade {
from {
opacity: 0;
transform: scale(0) rotate(90deg);
}
to {
opacity: 1;
transform: scale(1);
}
}
JavaScript code:
const navSlide = () => {
const burger = document.querySelector('.burger');
const nav = document.querySelector('.nav-links');
const navLinks = document.querySelectorAll('.nav-links li');
const navigationItems = document.querySelectorAll('.nav-links a');
// if there is no animation on the links add the one that I have declared in CSS
burger.addEventListener('click', () => {
nav.classList.toggle('nav-active');
navLinks.forEach((link, index) => {
// I think the problem is in this if/else statement
if (link.style.animation) {
link.style.animation = ''
}
else {
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 + 0.5}s`;
}
});
// toggle the burger animation that I have declared in CSS
burger.classList.toggle('toggle');
// close the menu and the burger on clicking on one of the navigational links
navigationItems.forEach((navigationItem) => {
navigationItem.addEventListener("click", () => {
nav.classList.remove('nav-active');
burger.classList.remove('toggle');
});
});
});
}
navSlide();
Thank you, grateful for any help.
I just noticed that I forgot to remove the animation in the event listener that had the task to close the navigation bar and the problem was occurring because the navigation bar was closing but the navigational links still had the animation on them. So to solve my problem I just needed to add two lines of code that will remove the animation when the navigation bar closes.
Here is the solution :
navigationItems.forEach((navigationItem) => {
navigationItem.addEventListener("click", () => {
//I was missing the next two lines of code to remove the animation from my navigational links when the navigational bar closes
navLinks.forEach((link) => {
link.style.animation = ''
});
nav.classList.remove('nav-active');
burger.classList.remove('toggle');
});
});
Problem solved.
Related
I have a section in my html with height=100vh. I want to show an animation of a text once user clicks anywhere on the window. Basically I want to keep triggering multiple animations over click event. I've achieved this using the following code:
const p1 = document.querySelector('.one p')
const p2 = document.querySelector('.two p')
const section1 = document.querySelector('.one')
const section2 = document.querySelector('.two')
window.addEventListener('click', () => {
p1.classList.add('animation')
if (p1.classList.contains('animation')) {
setTimeout(() => {
window.addEventListener('click', () => {
section1.classList.add('animation')
section2.classList.add('animation')
if (section1.classList.contains('animation')) {
setTimeout(() => {
window.addEventListener('click', () => {
p2.classList.add('animation')
}, {once:true})
}, 500)
}
}, {once:true})
}, 1000)
}
}, {once:true})
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
height: 100vh;
}
section {
width: 100%;
height: 100vh;
color: white;
font-family: sans-serif;
font-size: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.one {
background-color: indianred;
transition: 500ms ease-in;
}
.one.animation {
transform: translateY(-100vh);
}
.one p {
transform: translateX(-100vw);
transition: 1s ease-in;
}
.one p.animation {
transform: translateX(0);
}
.two {
background-color: grey;
transition: 500ms ease-in;
}
.two.animation {
transform: translateY(-100vh);
}
.two p {
transform: translateX(-100vw);
transition: 1s ease-in;
}
.two p.animation {
transform: translateX(0);
}
<body>
<section class="one">
<p>Section One</p>
</section>
<section class="two">
<p>Section Two</p>
</section>
</body>
But once all animations are done and I refresh the page, the applied animations remains and the page doesn't reload to it's initial look. I want to know why that is happening.
Also I would appreciate if I'm suggested with a different approach to achieve the requirements. Because the way I'm achieving it, I'm nesting the click event listener which doesn't seem to be efficient and a good practice.
CodePen
I am trying to replicate the letter flipping animation in Wordle. But I cannot manage the smooth chaining/sequencing. How can I fix it? (I guess I need to use the JS Promise feature, but yet to understand that concept.)
function myFunction() {
var tiles = document.getElementsByClassName("inner");
var myArray = Array.from(tiles);
myArray.map(function (tile) {
tile.classList.add("flip-in");
// tile.style.setProperty("--flipColor", "green");
tile.addEventListener(
"animationend",
() => {
tile.classList.remove("flip-in");
tile.style.backgroundColor = "green";
tile.classList.add("flip-out");
},
{
once: true
}
);
return;
});
}
var flipper = document.getElementById("flipper");
flipper.addEventListener("click", myFunction);
You've essentially done it but probably overengineered it a bit. The wordle animation is pretty simple to accomplish using only one animation.
First, let's take care of the CSS animation. Since we will only use one animation for the entire flip we can rename it "flip".
To simulate the card "flipping" we can adjust the scale on the height rather than flipping it. At the same time, we can also apply the background color change.
We can also remove the animation-delay styles. We will apply these dynamically in the JS.
#keyframes flip {
0% {
transform: scaleY(1);
}
50% {
background: white;
transform: scaleY(0);
}
100% {
transform: scaleY(1);
background: green;
}
}
We have to mark the animation as fill-mode: forwards
.flip {
animation: flip 500ms ease forwards;
}
Next, we can simplify the JS to only apply the class. Do some renaming to easier understand what everyone is and does. And here we can also dynamically apply the animation delay based on the index of the tile. This way we will support all different number of tiles.
function applyFlip() {
var tiles = document.getElementsByClassName("inner");
var tilesArray = Array.from(tiles);
tilesArray.map(function (tile, i) {
tile.classList.add("flip");
tile.style.animationDelay = `${i * 100}ms`;
});
}
var flipper = document.getElementById("flipper");
flipper.addEventListener("click", applyFlip);
Here's a working snippet:
function applyFlip() {
var tiles = document.getElementsByClassName("inner");
var tilesArray = Array.from(tiles);
tilesArray.map(function (tile, i) {
tile.classList.add("flip");
tile.style.animationDelay = `${i * 100}ms`;
});
}
var flipper = document.getElementById("flipper");
flipper.addEventListener("click", applyFlip);
.container {
width: 540px;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 2px;
}
.inner {
display: flex;
justify-content: center;
align-items: center;
font-size: 40px;
color: black;
width: 100px;
height: 100px;
background: white;
border: 2px solid black;
border-radius: 6px;
}
button {
margin: 50px;
font-size: 24px;
padding: 4px;
}
.flip {
animation: flip 500ms ease forwards;
}
#keyframes flip {
0% {
transform: scaleY(1);
}
50% {
background: white;
transform: scaleY(0);
}
100% {
transform: scaleY(1);
background: green;
}
}
<div class="container">
<div class="inner">S</div>
<div class="inner">T</div>
<div class="inner">A</div>
<div class="inner">C</div>
<div class="inner">K</div>
</div>
<button id="flipper"> Flipper </button>
I am trying to create a foldable menu, through CSS and JS. Css works (almost) correctly (the menu folds and unfolds, even if the class is not correctly applied) but not the JS code, which should change the innerText of the <li> acting as a button from ">" to "<" and opposite.
I have been messing around with js code for a while (making sure that document.getElementById is not undefined), but neither element.innerText or element.innerHTML seem to work properly.
I have two questions:
When applying an animation, and having two classes, shouldn't respect both classes (I mean, the navbar should be red)? Should I add nav class AFTER the animation is done, or fill the navbar in red color through the animation?
Why does ignore InnerText/InnerHTML instructions?? I have debugged the code and definitely goes through that instruction and I cannot understand why the change is not done...
var navButton;
var navbar;
const init = ()=>{
navbar = document.getElementById("navbar");
navButton = document.getElementById("foldButton");
navButton.addEventListener('click', function(){
if(navbar.className==="init nav" || navbar.className==="fade-out-right nav"){
navButton.InnerText=`<p><</p>`;
toggleFold();
}
else{
navButton.InnerText=`<p>></p>`;
toggleFold();
}
});
}
const toggleFold = () => {
if(navbar.className==="init nav" || navbar.className==="fade-out-right nav"){
navbar.className="fade-in-left nav";
}else{
navbar.className="fade-out-right nav";
}
};
* {
margin: 0;
padding: 0;
}
html {
box-sizing: border-box;
font-size: 62.5%;
scroll-behavior: smooth;
}
/* Base styles */
.nav {
display: flex;
justify-content: flex-end;
position: fixed;
top: 0;
left: 0;
width: 4%;
background: red;
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.4);
z-index: 10;
}
.nav-list {
display: flex;
margin-right: 2rem;
list-style: none;
}
.nav-list li {
display: block;
font-size: 2.2rem;
padding: 2rem;
}
.nav-list a{
color:black
}
.nav-list a:hover {
background: blue;
}
.fade-in-left {
animation-name: fade-in-left;
animation-duration: 2s;
animation-timing-function: ease;
animation-fill-mode: forwards;
}
#keyframes fade-in-left {
from {
opacity: 1;
transform: translateX(-4%);
}
to {
opacity: 1;
transform: translateX(305px);
}
}
.fade-out-right {
animation-name: fade-out-right;
animation-duration: 2s;
animation-timing-function: ease;
animation-fill-mode: forwards;
}
#keyframes fade-out-right {
from {
opacity: 1;
transform: translateX(305px);
}
to {
opacity: 1;
transform: translateX(-4%);
}
}
<body onload="init()">
<nav id="navbar" class="init nav">
<ul class='nav-list'>
<li><a href='#welcome-section'>About</a></li>
<li><a href='#projects'>Work</a></li>
<li><a href='#contact'>Contact</a></li>
<li id="foldButton"><p>></p></li>
</ul>
</nav>
</body>
Thank you for helping me out.
Apart from my low IQ for the bad typo, whenever the color is not filled when using an animation, use internal container instead (in my case, I filled ul instead of navbar).
I'm trying to make a slide menu but I don't know how would I close it back.
Basically i'm trying to do something like this website https://zero.nyc/
const menu = document.querySelector('aside');
const nav = document.querySelector('nav');
menu.addEventListener('click', () => {
nav.style.marginLeft = '0';
menu.style.left = '97vw';
})
aside {
border-right: 1px solid #e2e2e2;
width: 3vw;
height: 100vh;
line-height: 100vh;
position: fixed;
top: 0;
left: 0;
transition: 1s;
}
nav {
width: 97vw;
height: 100vh;
margin-left: -100vw;
transition: 1s;
}
<aside>
menu
</aside>
<nav>
<ul>
<li>Home</li>
<li>ABOUT</li>
<li>Contact</li>
</ul>
</nav>
Use css classes and toggle them on or off on in your click method instead of setting the styles through javascript.
Snippet:
const menu = document.querySelector('aside');
const nav = document.querySelector('nav');
menu.addEventListener('click', () => {
nav.classList.toggle("margin");
menu.classList.toggle("left");
})
aside {
border-right: 1px solid #e2e2e2;
width: 3vw;
height: 100vh;
line-height: 100vh;
position: fixed;
top: 0;
left: 0;
transition: 1s;
-webkit-transition: 1s;
-ms-transition: 1s;
-moz-transition: 1s;
}
nav {
width: 97vw;
height: 100vh;
margin-left: -100vw;
transition: 1s;
-webkit-transition: 1s;
-ms-transition: 1s;
-moz-transition: 1s;
}
.margin {
margin-left: 0;
}
.left {
left: 97vw;
}
<aside>
menu
</aside>
<nav>
<ul>
<li>Home</li>
<li>ABOUT</li>
<li>Contact</li>
</ul>
</nav>
I would create css classes to toggle within your click event handler instead of manually changing the styles.
On click you're basically going to:
Grab the elements you want to change
Toggle the classes using classList.toggle (if you're not worried about < ie9)
Benefit of using classes is once you remove it, the element simply reverts. It's also more maintainable because you don't have to keep track of the styles you change.
Use transform: translateX() instead of margin. You also have to write some state of your menu i.e. opened/closed. You can use a css class for that, and in your eventListener you can check if menu is opened, then onClick animate from translateX(0) to translateX(-100%)and remove opened class.
Or you can write styles with transitions, or #keyframes animations, everything will work. Just build the idea of storing opened state first.
I feel dumb for asking this. Let's start off by I used a W3 responsive template. I love everything about it and even incorporated the single page application design aspect using AngularJS. Now here's my one question.
When whoever is on a mobile device using the website, they get a menu option that opens a sidenav. This animation is called "w3-animate-left" and it pulls the nav from -300px to 0px as shown below. Now what I want to do is make one that animates from 0px to -300px and incorporate it to my existing CSS.
.w3-animate-left {
position: relative;
-webkit-animation: animateleft 0.4s;
animation: animateleft 0.4s;
}
#-webkit-keyframes animateleft {
from {
left: -300px;
opacity: 0;
}
to {
left: 0;
opacity: 1;
}
}
#keyframes animateleft {
from {
left: -300px;
opacity: 0;
}
to {
left: 0;
opacity: 1;
}
}
Then I ran into another obstacle, the div containing the class call to the CSS above incorporates it in the class as shown below.
<div class="w3-sidenav w3-white w3-card-2 w3-animate-left w3-hide-medium w3-hide-large" style="display:none" id="mySidenav" ng-controller="mainController">
<i class="fa fa-birthday-cake"></i> PACKAGES
<i class="fa fa-child"></i> OPEN PLAY
<i class="fa fa-camera-retro"></i> GALLERY
<i class="fa fa-envelope"></i> CONTACT
</div>
Here's the JavaScript as well:
<script>
// Toggle between showing and hiding the sidenav when clicking the menu icon
var mySidenav = document.getElementById('mySidenav');
function w3_open() {
if (mySidenav.style.display === 'block') {
mySidenav.style.display = 'none';
} else {
mySidenav.style.display = 'block';
}
}
function w3_close() {
mySidenav.style.display = 'none';
}
</script>
Now the problem here is even if I made the edit to the W3 CSS that I wanted, how would I be able to incorporate it so that when they click to close, it does the custom animation that I asked?
Also how can I add it that if they click somewhere off the screen, it exits the side nav? Thank you in advance!
var _open = document.getElementById("open"),
_navigation = document.getElementById("navigation");
_open.onclick = function() {
_navigation.classList.toggle("openNav");
}
* {
margin: 0;
padding: 0; }
#open {
position: absolute;
right: 0;
cursor: pointer; }
#navigation {
background: black;
width: 300px;
height: 100vh;
transform: translateX(-300px);
transition: all 0.3s linear; }
#navigation.openNav {
transform: translateX(0); }
<a id="open">Open/Close</a>
<div id="navigation"></div>
This is my method on creating navigation animation and not based on someone's work. This will not work in old browsers.
The technique you just need is the transform, transition and adding class. You don't need to use animation.
By adding class of openNav, it will change the value of transform: translateX and it allows the navigation to be shown in browser.
Take note that you can't animate display attribute in css. You can use opacity for fadeIn/fadeOut.
Hope it helps. Cheers!