I encountered a problem when animating slide-in and slide-out.
The number "0" has to move and fade to left and the number "7" has to arrive from right when clicking on "Click" and the inverse when clicking back, indefinitely.
I have used 4 classes : "move-in", "move-out", "move-in-rev" and "move-in-rev" containing the animation with "animationend" that removes it to repeat the animation over and over.
Unfortunally, my skills stop here because the removing of the class removes also the property "animation-fill-mode : towards" which leads to the initial state (opacity = 1 on "0" and not on the "7") and I don't see how to handle it.
Could someone help me please ?
page = 0;
num1 = document.getElementById("num2-1");
num2 = document.getElementById("num2-2");
document
.querySelector("a")
.addEventListener("click", function(event) {
//Go from 0 to 7
if (page == 0) {
//0 go to left and fade
num1.classList.add("move-out");
num1.addEventListener('animationend', function() {
num1.classList.remove("move-out");
});
//7 arrives from right
num2.classList.add("move-in");
num2.addEventListener('animationend', function() {
num2.classList.remove("move-in");
});
page = (++page);
}
//Go from 7 to 0
else {
//0 arrives from left
num1.classList.add("move-out-rev");
num1.addEventListener('animationend', function() {
num1.classList.remove("move-out-rev");
});
//0 go to right and fade
num2.classList.add("move-in-rev");
num2.addEventListener('animationend', function() {
num2.classList.remove("move-in-rev");
});
page = (--page);
};
});
#keyframes slide-in {
from {
transform: translateX(0%);
opacity: 0;
}
to {
transform: translateX(-100%);
opacity: 1;
}
}
#keyframes slide-out {
from {
transform: translateX(0%);
opacity: 1;
}
to {
transform: translateX(-100%);
opacity: 0;
}
}
body,html {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
#nums {
display: flex;
flex-direction: row;
}
#num2-1 {
opacity: 1;
}
#num2-2 {
opacity: 0;
}
a {
text-decoration: none;
cursor: pointer;
user-select: none;
}
a:hover {
color: red;
}
.move-in {
animation : slide-in 0.5s;
animation-fill-mode: forwards;
}
.move-in-rev {
animation : slide-in 0.5s reverse;
animation-fill-mode: forwards;
}
.move-out {
animation : slide-out 0.5s;
animation-fill-mode: forwards;
}
.move-out-rev {
animation : slide-out 0.5s reverse;
animation-fill-mode: forwards;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css" />
<title>transition</title>
</head>
<body>
<div id="nums">
<div id="num1">8</div>
<div id="num2-1">0</div>
<div id="num2-2">7</div>
</div>
<div id="button">
<a id="test">
Click
</a>
</div>
<script src="index.js"></script>
</body>
</html>
I have a simple CSS3 Flipbox which I'm trying to re-use multiple times. The best would be if each element has a unique ID, and each of them would be separately triggered on click.
At the moment all the elements are being triggered on click.
What would be the best approach without just doubling the code?
Here demo of the code:
https://codepen.io/baidoc/pen/LYeqwxe
let cardTransitionTime = 500;
let $card = $('.card');
let switching = false;
$('.card').click(flipCard);
function flipCard() {
if (switching) {
return false;
}
switching = true;
$card.toggleClass('is-switched');
window.setTimeout(function () {
$card.children().children().toggleClass('is-active');
switching = false;
}, cardTransitionTime / 2);
}
I havent thought your transitionTime example through and left it out in a more simplified example, but for the other part you can simply use the event argument which is handed over to your called function per default:
$('.card').click(flipCard);
function flipCard(ev) {
ev.currentTarget.classList.toggle('is-switched');
}
Here I created an event listener on a parent element to the cards. No matter how many cards there are this event will be fired.
When you click either the header or one of the parent divs the nearest <div class="card"> will be found. Now the class name can be toggled for that element.
// event listener for any of the cards
document.getElementById('cardwrapper').addEventListener('click', e => {
// what card was clicked
let card = e.target.closest('.card');
// toogle class name 'is-switched'
card.classList.toggle('is-switched');
});
$card-transition-time: 0.5s;
.card {
perspective: 600px;
position: relative;
cursor: pointer;
}
.card.is-switched .card__wrapper {
animation: rotate .5s linear both;
}
.card__wrapper {
transform-style: preserve-3d;
animation: rotate-inverse .5s linear both;
}
.card__side {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.card__side.is-active {
position: static;
}
.card__side--back {
transform: rotateY(180deg);
}
#keyframes rotate {
0% {
transform: rotateY(0);
}
70% {
transform: rotateY(200deg);
}
100% {
transform: rotateY(180deg);
}
}
#keyframes rotate-inverse {
0% {
transform: rotateY(180deg);
}
70% {
transform: rotateY(-20deg);
}
100% {
transform: rotateY(0);
}
}
// ignore
* {
box-sizing: border-box;
}
body {
background-color: #fff;
text-align: center;
padding: 1.5rem;
}
h1,
h2 {
margin: 0;
}
.card {
margin: 2rem auto;
max-width: 350px;
}
.card__side {
padding: 1em;
border-radius: 5px;
color: white;
background-color: #ff4136;
}
.card__side--back {
background-color: #0074d9;
}
<div id="cardwrapper">
<div class="card" id="item1">
<div class="card__wrapper">
<div class="card__side is-active">
<h1>Card 1</h1>
</div>
<div class="card__side card__side--back">
<h2>Card 1</h2>
</div>
</div>
</div>
<div class="card" id="item2">
<div class="card__wrapper">
<div class="card__side is-active">
<h1>Card 2</h1>
</div>
<div class="card__side card__side--back">
<h2>Card 2</h2>
</div>
</div>
</div>
</div>
Problem: NavBar disappears after scrollIntoView() used on mobile view.
Related JS
function disableScroll() {
window.addEventListener('DOMMouseScroll', preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener('touchmove', preventDefault, wheelOpt); // mobile
window.addEventListener('keydown', preventDefaultForScrollKeys, false);
}
function enableScroll() {
window.removeEventListener('DOMMouseScroll', preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener('touchmove', preventDefault, wheelOpt);
window.removeEventListener('keydown', preventDefaultForScrollKeys, false);
}
function scroll_to_chapters() {
const chapters = document.getElementsByClassName("chapter_list")[0];
if (nav.classList.contains("nav-active")) {
navLinks.forEach((link) => {
if (link.style.animation) {
link.style.animation = '';
}
});
enableScroll();
burger.classList.toggle('toggle');
nav.classList.toggle('nav-active');
}
chapters.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
const navSlide = ()=> {
burger.addEventListener('click', ()=> {
//Toggle NavBar
nav.classList.toggle('nav-active');
//Animate Links
navLinks.forEach((link, index)=> {
if (link.style.animation) {
enableScroll();
link.style.animation = '';
}
else {
disableScroll();
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 + 0.5}s`;
}
});
burger.classList.toggle('toggle');
});
}
I'm not quite sure but there might be a problem with the disabling and enabling scrolling. I didn't want mobile users to be able to scroll while the navbar menus are opened.
Navbar HTML
<div class="wrapper">
<div class="fix">
<header>
<div class="container">
<nav>
<img src="{% static 'img/northernlogo.png' %}" class="logo" alt="logo">
<ul class="nav-links">
<li>Home</li>
<li>Chapters</li>
<li>About</li>
<li>Links</li>
</ul>
<div class="burger">
<div class="line1"></div>
<div class="line2"></div>
<div class="line3"></div>
</div>
</nav>
</div>
</header>
</div>
</div>
Related CSS
.container {
width: 100%;
margin: 0 0;
}
.container::after {
content: '';
display: grid;
clear: both;
}
.burger {
display: none;
margin-top: 20px;
cursor: pointer;
}
.burger div {
width: 25px;
height: 3px;
background: #EEEEEE;
margin: 5px;
transition: all 0.3s ease;
}
#media only screen and (max-width: 800px) {
.wrapper {
overflow: hidden;
position: relative;
}
.nav-links {
-webkit-box-shadow: inset 0 0.5em 1.5em -0.5em black;
position: absolute;
right: 0;
height: 100%;
top: 70px;
background-color: #3a345c;
z-index: 99;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
transform: translateX(100%);
transition: transform 0.5s ease-in;
}
.nav-links li {
opacity: 0;
padding-bottom: 50px;
padding-top: 50px;
}
.nav-links li a {
font-size: 20px;
font-weight: bold;
}
.burger {
display: block;
}
}
Picture of the Error
Before
After
How to Reproduce
Open the website on mobile view. Open the navbar from the home menu and click chapters. It'll take you to the chapters but now the navbar is gone.
Editor's note:
The accepted answer features a self-contained reproducible example.
This happens because .nav-links are position: absolute with:
a position: static (by default) parent .fix, and
a position: relative grandparent .wrapper.
In this case, scrollIntoView({block: 'start', ...}) scrolls .fix out of view of .wrapper before scrolling .wrapper itself.
{block: 'center', ...} does the same.
{block: 'end', ...} and {block: 'nearest', ...} don't.
You can't scroll .fix back into view because .wrapper has overflow: hidden.
Solution
Make .fix have position: relative and make .nav-links have height: 100vh.
.fix {
position: relative;
}
.nav-links {
height: 100vh;
}
Run this code snippet to reproduce the problem, and click on the toggle to try with answer:
const burger = document.querySelector('.burger');
const nav = document.querySelector('.nav-links');
const navLinks = document.querySelectorAll('.nav-links li');
// left: 37, up: 38, right: 39, down: 40,
// spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
var keys = {
37: 1,
38: 1,
39: 1,
40: 1
};
function preventDefault(e) {
e.preventDefault();
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
// modern Chrome requires { passive: false } when adding event
var supportsPassive = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
get: function() {
supportsPassive = true;
}
}));
} catch (e) {}
var wheelOpt = supportsPassive ? {
passive: false
} : false;
var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
// call this to Disable
function disableScroll() {
window.addEventListener('DOMMouseScroll', preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener('touchmove', preventDefault, wheelOpt); // mobile
window.addEventListener('keydown', preventDefaultForScrollKeys, false);
}
// call this to Enable
function enableScroll() {
window.removeEventListener('DOMMouseScroll', preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener('touchmove', preventDefault, wheelOpt);
window.removeEventListener('keydown', preventDefaultForScrollKeys, false);
}
function read_more_less(button) {
var x = $('#more');
$(button).find('i').remove();
if ($(button).text().trim() === 'Read more') {
$(button).html($('<i/>', {
class: 'fas fa-sort-down'
})).append(' Read less');
x.fadeIn();
} else {
$(button).html($('<i/>', {
class: 'fas fa-sort-up'
})).append(' Read more');
x.fadeOut();
}
}
function scroll_to_chapters() {
const chapters = document.getElementsByClassName("chapter_list")[0];
if (nav.classList.contains("nav-active")) {
navLinks.forEach((link) => {
if (link.style.animation) {
link.style.animation = '';
}
});
enableScroll();
burger.classList.toggle('toggle');
nav.classList.toggle('nav-active');
}
chapters.scrollIntoView({
block: 'start',
behavior: 'smooth'
})
}
const navSlide = () => {
burger.addEventListener('click', () => {
//Toggle NavBar
nav.classList.toggle('nav-active');
//Animate Links
navLinks.forEach((link, index) => {
if (link.style.animation) {
enableScroll();
link.style.animation = '';
} else {
disableScroll();
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 + 0.5}s`;
}
});
burger.classList.toggle('toggle');
});
}
navSlide();
function toggleAnswer(button) {
var wrapper = document.querySelector('.wrapper');
wrapper.classList.toggle('answer');
button.innerHTML = wrapper.classList.contains('answer') ? 'Click to try without answer' : 'Click to try with answer';
}
body {
margin: 0;
background: #212121;
font-family: 'Glory', serif !important;
}
.container {
width: 100%;
margin: 0 0;
}
.container::after {
content: '';
display: grid;
clear: both;
}
.burger {
display: none;
margin-top: 20px;
cursor: pointer;
}
.burger div {
width: 25px;
height: 3px;
background: #EEEEEE;
margin: 5px;
transition: all 0.3s ease;
}
header {
background: #3a345c;
}
nav {
justify-content: space-around;
display: flex;
}
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav a:not([data-attr="img"]) {
color: #eeeeee;
text-decoration: none;
text-transform: uppercase;
font-size: 15px;
}
#media only screen and (max-width: 800px) {
.wrapper {
overflow: hidden;
position: relative;
}
.nav-links {
-webkit-box-shadow: inset 0 0.5em 1.5em -0.5em black;
position: absolute;
right: 0;
height: 100%;
top: 70px;
background-color: #3a345c;
z-index: 99;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
transform: translateX(100%);
transition: transform 0.5s ease-in;
}
.nav-links li {
opacity: 0;
padding-bottom: 50px;
padding-top: 50px;
}
.nav-links li a {
font-size: 20px;
font-weight: bold;
}
.burger {
display: block;
}
}
.nav-active {
transform: translateX(0%);
}
#keyframes navLinkFade {
from {
opacity: 0;
transform: translateX(50px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* Work within Stack Snippets window height */
.nav-links li {
padding: 10px;
}
/* Answer */
.answer .fix {
position: relative;
}
.answer .nav-links {
height: 100vh;
}
<html>
<head>
</head>
<body>
<div class="wrapper">
<div class="fix">
<header>
<div class="container">
<nav>
<a style="height: 70px;"></a>
<ul class="nav-links">
<li>Home</li>
<li>Chapters</li>
<li>About</li>
<li>Links</li>
</ul>
<div class="burger">
<div class="line1"></div>
<div class="line2"></div>
<div class="line3"></div>
</div>
</nav>
</div>
</header>
</div>
<button onclick="toggleAnswer(this);">Click to try with answer</button>
<div style="height: 500px;">
</div>
<div class="chapter_list">
<h2 style="color: white; padding-left: 10px;">Chapter list</h2>
</div>
</div>
</body>
</html>
I do not know what is wrong with scrollIntoView, but I remark that when I move the chapiters parts to be a direct child of body, that problem disapear.
However, there is a way to do that same behavior without any js code:
In your CSS code, add scroll-behavior: smooth; to the root.
Add an id to the chapiter : <div id="chap_id" class="chapter_list">
Turn the href attribut of chapiter link in to : an page#id link : <a href="page_chapters#chap_id">
You can find a doc on scroll-behavior here.
So you are using the body as overscroll and there is also a wrapper with the same height (and it's basically the same as the body) with an overscroll hidden.
Your menu you let slide in has a 100% height, so the bottom is at nav:height + 100%. I assume the scroll into view first scrolls the wrapper (as it overflows the nav:height) and then it scrolls the body. Making the nav-bar disappear, as the overflow is hidden.
You can fix this by adjusting the menu height to make it actually 100% in height: e.g. CSS: calc(100% - 70px) or using flex-box and let the menu flex:1.
You have to fix you navbar using "position: fixed" property. Add & Modify given CSS
Here is the result
#media only screen and (max-width: 800px) {
/*ADD This*/
.fix {
position: fixed;
width: 100%;
z-index: 1;
}
/*modify this*/
.nav-links {
height: 100vh;
}
.content {
margin-top: 90px;
}
}
I built a carousel for some images. I need my 'next' and 'previous' buttons to use the CSS animation that I assigned them in my javascript function for their event listeners.The animation will only play for one click, and when the buttons are clicked again to navigate the carousel the animation doesn't play. I need the buttons to grow and shrink for every click.
Here's the CSS:
.carousel-actions {
position: absolute;
display: flex;
justify-content: space-between;
width: 105%;
top: 30%;
}
.carousel-actions button {
padding: 30px 50px;
background-color: rgba(255, 255, 255, 0.329);
font-weight: 900;
cursor: pointer;
border-radius: 100px;
border: 0;
font-size: 60px;
color: black;
outline: none;
}
#keyframes grow {
0% {
transform: scale(1);
}
50% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
Here's the HTML:
<div class="carousel-actions">
<button id="prev" aria-label="Previous Slide"><</button>
<button id="next" aria-label="Next Slide">></button>
</div>
Here's the JS:
const slides = document.querySelectorAll(".carousel-item");
const totalSlides = slides.length;
let slidePosition = 0;
console.log(totalSlides);
const next = document.getElementById("next");
const prev = document.getElementById("prev");
function hideAllSlides() {
for (const slide of slides) {
slide.classList.remove("carousel-item-visible") &&
slide.classList.add("carousel-item-hidden");
}
}
function nextSlide() {
hideAllSlides();
if (slidePosition === totalSlides - 1) {
slidePosition = 0;
} else {
slidePosition++;
}
slides[slidePosition].classList.add("carousel-item-visible");
next.style.animation = "grow 1s";
}
function prevSlide() {
hideAllSlides();
if (slidePosition === 0) {
slidePosition = totalSlides - 1;
} else {
slidePosition--;
}
slides[slidePosition].classList.add("carousel-item-visible");
prev.style.animation = "grow 1s";
}
next.addEventListener("click", nextSlide);
prev.addEventListener("click", prevSlide);
The problem is seen because once the system has played the animation it thinks 'well, I've played it'. Setting it to the same again does not make it play again.
To get round this you can unset the animation when it has finished.
In your code add an event listener for the animationend event.
Here's a simplified example:
const div = document.querySelector('div');
div.addEventListener('click', function() { div.style.animationName='grow';});
div.addEventListener('animationend', function() { div.style.animationName='';});
div {
width: 100px;
height: 100px;
background-color: blue;
animation-duration: 1s;
animation-iteration-count: 1;
}
#keyframes grow {
0% {
transform: scale(1);
}
50% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
<div></div>
I looked for a solution in the web, without success. I don’t understand why this code fires only once:
$("#d").click(function() {
var h = $(".cont");
var f = h.offset();
if (f.left < 1) {
h.addClass('anim');
} else {
h.addClass('anim2');
}
});
body{
margin: 0;
}
#d {
width: 50px;
height: 50px;
background: #999;
}
.cont {
width: 200px;
height: 200px;
opacity: 1;
background: #333;
position: absolute;
-webkit-transition: all 1s ease-in-out;
}
.anim {
-webkit-transform: translate(50px, 0px);
}
.anim2 {
-webkit-transform: translate(0px, 0px);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="d"></div>
<div class="cont"></div>
Here’s a JSFiddle demo.
What you need is this
$("#d").click( function() {
var h = $(".cont");
var f = h.offset();
if (f.left < 1) {
h.addClass('anim');
h.removeClass('anim2');
} else {
h.addClass('anim2');
h.removeClass('anim');
}
});
after the first click you never remove the classes so they still have effect