I've come up with this simple code, but for some reason, it does not always work exactly the way I would like it to.
Here's what I have. There are tiles that flip on hover. There are two possible scenarios and two reactions.
If I mouse is hovered over a tile, the tile is supposed to flip 180° and stay in this possition. This happens, if mouseleave happens in longer time than 1 second. After unhover, the tile should flip back to its original state.
If mouse is hovered over a tile and unhover immediately, the tile is supposed to do a full 180° flip before returning to its original state. This happens when mouse leaves in the 1 second timeframe. In this case, the JS scripts waits for the CSS transition to end before fliping the tile back.
Now, this works fine until the second scenario takes place. Then for some reason, the tile returns to its original state after a full transition despite the mouse not leaving the tile. Why is that? Any ideas on how to overcome this issue?
Thanks.
$(function() {
var timeoutId;
$(".tile").mouseenter(function() {
$(this).addClass("flip");
if (!timeoutId) {
timeoutId = window.setTimeout(function() {
timeoutId = null;}, 1000);
}
});
$(".tile").mouseleave(function() {
if (timeoutId) {
$(this).on('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}
else {
$(this).removeClass("flip");
}
});
})
body {
text-align: center;
background-color: #FFFFFF;
color: #454545;
}
.flex-container {
padding: 0;
margin: 0;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row;
justify-content: space-around;
}
.tile {
background: #454545;
margin: 0px;
font-weight: bold;
flex: 1 0 auto;
height: auto;
border: 1px solid #454545;
border-radius: 10%;
}
.tile:before {
content: '';
float: left;
padding-top: 100%;
}
.tile-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 10%;
transform-style: preserve-3d;
transition: transform 4s;
}
.tile.flip .tile-inner {
transform: rotate3d(-1, 1, 0, 180deg);
transition: transform 1s;
}
.tile-front, .tile-off, .tile-semi, .tile-rsemi, .tile-on {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10%;
}
.tile-front {
background-color: #1c1c1c;
background-size: 100%;
}
.tile-off {
background-color: #252525;
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-semi {
background: linear-gradient(to right bottom, #252525 50%, #dedede 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-rsemi {
background: linear-gradient(to right bottom, #dedede 50%, #252525 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-on {
background-color: #dedede;
transform: rotate3d(-1, 1, 0, 180deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<body>
<div class="flex-container">
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-semi"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-on"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-rsemi"></div>
</div>
</div>
</div>
</body>
You are sharing one variable and using it on all of the other tiles. So the timeout for tile 1 will also be with tile 2 when you mouseover it. You should be setting the timeouts for each one individually. Basic idea with data.
$(function() {
$(".tile").mouseenter(function() {
var elem = $(this);
elem.addClass("flip");
if (!elem.data('timeoutId')) {
var tid = window.setTimeout(function() {
elem.removeData('timeoutId')
}, 1000);
elem.data('timeoutId', tid)
}
});
$(".tile").mouseleave(function() {
var elem = $(this);
var timeoutId = elem.data('timeoutId')
if (timeoutId) {
window.clearTimeout(timeoutId);
elem.removeData('timeoutId')
elem.off('transitionend webkitTransitionEnd oTransitionEnd')
elem.on('transitionend webkitTransitionEnd oTransitionEnd', function() {
elem.removeClass("flip");
});
} else {
$(this).removeClass("flip");
}
});
})
body {
text-align: center;
background-color: #FFFFFF;
color: #454545;
}
.flex-container {
padding: 0;
margin: 0;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row;
justify-content: space-around;
}
.tile {
background: #454545;
margin: 0px;
font-weight: bold;
flex: 1 0 auto;
height: auto;
border: 1px solid #454545;
border-radius: 10%;
}
.tile:before {
content: '';
float: left;
padding-top: 100%;
}
.tile-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 10%;
transform-style: preserve-3d;
transition: transform 4s;
}
.tile.flip .tile-inner {
transform: rotate3d(-1, 1, 0, 180deg);
transition: transform 1s;
}
.tile-front,
.tile-off,
.tile-semi,
.tile-rsemi,
.tile-on {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10%;
}
.tile-front {
background-color: #1c1c1c;
background-size: 100%;
}
.tile-off {
background-color: #252525;
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-semi {
background: linear-gradient(to right bottom, #252525 50%, #dedede 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-rsemi {
background: linear-gradient(to right bottom, #dedede 50%, #252525 50%);
transform: rotate3d(-1, 1, 0, 180deg);
}
.tile-on {
background-color: #dedede;
transform: rotate3d(-1, 1, 0, 180deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<body>
<div class="flex-container">
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-semi"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-on"></div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front"></div>
<div class="tile-rsemi"></div>
</div>
</div>
</div>
</body>
The problem is that when you run:
if (timeoutId) {
$(this).on('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}
The event stays in the element indefinitely. So, next time you hover the element, when it finishes transition, this event triggers again.
What you probably want is to run the event only once:
if (timeoutId) {
$(this).once('transitionend webkitTransitionEnd oTransitionEnd', function () {
$(this).removeClass("flip");
window.clearTimeout(timeoutId);
timeoutId = null;
});
}
Related
I'm using HTML/CSS and vanilla JS. I'm trying to create a simple carousel. However, whenever I click the 'next' button, the following slides show up under the last one. I'm not sure why this is happening. Code snippets are below. Can someone explain why this is happening?
By the way, I have not optimized this page for media queries yet, so it might look a little weird on smaller screens.
const buttons = document.querySelectorAll("[data-carousel-btn]");
buttons.forEach((button) => {
button.addEventListener("click", () => {
const offset = button.dataset.carouselBtn === "next" ? 1 : -1;
const slidesContainer = button
.closest("[data-carousel]")
.querySelector("[data-carousel-slides");
const slides = slidesContainer.querySelectorAll("[data-carousel-slide]");
const activeSlide = slidesContainer.querySelector("[data-active]");
const activeSlideIndex = [...slides].indexOf(activeSlide);
const nextSlideIndex = activeSlideIndex + offset;
if (nextSlideIndex < 0) {
slides[slides.length + nextSlideIndex].dataset.active = true;
return delete activeSlide.dataset.active;
}
if (nextSlideIndex >= slides.length) {
slides[0].dataset.active = true;
return delete activeSlide.dataset.active;
}
slides[nextSlideIndex].dataset.active = true;
return delete activeSlide.dataset.active;
});
});
.carouselContainer {
width: 1000px;
height: 500px;
position: relative;
display: flex;
justify-content: center;
border-style: dashed;
border-color: #010043;
}
.carouselContainer>ul {
padding: 0;
margin: 0;
list-style: none;
}
.slide {
display: flex;
width: 400px;
height: 500px;
justify-content: center;
align-items: center;
inset: 0;
opacity: 0;
transition-property: opacity;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
transition-delay: 200ms;
border-style: dashed;
border-color: var(--magenta6);
}
.slide[data-active] {
opacity: 1;
z-index: 1;
transition-delay: 0ms;
}
.slideContent {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
width: 300px;
height: 425px;
border-style: dashed;
border-color: #010043;
}
.slideContent .slideImg {
margin: 0;
width: 200px;
height: 200px;
border-radius: 100px;
z-index: 10px;
}
.slideContent .slideTxt {
border: magenta;
border-style: dashed;
height: 500px;
}
.carousel-button {
position: absolute;
background: none;
border: none;
outline: none;
font-size: 4rem;
top: 50%;
transform: translateY(-50%);
z-index: 2;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
padding: 0 0.5rem;
border-radius: 0.25rem;
background-color: rgba(0, 0, 0, 0.1);
transition: 0.5s;
}
.carousel-button:hover,
.carousel-button:focus {
background-color: rgba(0, 0, 0, 0.3);
color: #fff;
}
.carousel-button[data-carousel-btn="prev"] {
left: 1rem;
}
.carousel-button[data-carousel-btn="next"] {
right: 1rem;
}
<section class="row4 animateOnScroll" style="margin: 0 100px 100px 100px;">
<h2>don't just take it from us!</h2>
<div class="carouselContainer" data-carousel>
<button class="carousel-button" data-carousel-btn="prev">
❮
</button>
<button class="carousel-button" data-carousel-btn="next">
❯
</button>
<div class="carouselSlides">
<ul data-carousel-slides>
<li class="slide" data-carousel-slide data-active>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</li>
<li class="slide" data-carousel-slide>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</li>
<li class="slide" data-carousel-slide>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</ul>
</div>
</div>
</section>
part of your problem is your ul element is unstyled, its child li elements are going to stack as they normally do on top of each other. giving the ul element display: flex; will put your li's side by side.
If I were you, I would review each nested element of my tree and figure out its purpose, then remove it if not necessary. for example, div.carouselSlides does not seem like its serving any purpose that the ul could not do itself, at least in this small example.
Also, looking at an established project for implementation ideas (or just using it) might be a good idea . https://swiperjs.com/ is very established with powerful config options
Basically there should be 2 containers:
The element that is the parent of the <button>s and the "frame" that holds each slide. In the example it is article.box.
The element that is the parent of each div.slide in the example is section.frame.
Each container should be position: relative and all children of said containers should be position: absolute. Doing so will:
provide precision positioning of the <button>s and section.frame within the perimeters of article.box
allow all div.slide to hide underneath the visible layers (z-index:0+) with z-index: -1 and be visible with .active class at z-index: 1.
The JavaScript is optional, it's just written better but function should be basically the same.
View in full page mode, the image placeholder service does not have dynamic images.
const data = [
{img:"https://placem.at/people?random=1", cap:"People 1"},
{img:"https://placem.at/places?random=1", cap:"Places 2"},
{img:"https://placem.at/things?random=1", cap:"Things 3"}
];
const slides = genSlides(data);
const box = document.querySelector('.box')
const buttons = box.querySelectorAll("button");
buttons.forEach((button) => {
button.addEventListener("click", function(event) {
const offset = button.classList.contains("next") ? 1 : -1;
const active = box.querySelector(".active");
const actIdx = slides.indexOf(active);
const nextIdx = actIdx + offset;
active.classList.remove("active");
if (nextIdx < 0) {
return slides[slides.length + nextIdx].classList.add("active");
}
if (nextIdx >= slides.length) {
return slides[0].classList.add("active");
}
return slides[nextIdx].classList.add("active");
});
});
function genSlides(array) {
const frame = document.querySelector(".frame");
array.forEach(slide => {
frame.insertAdjacentHTML("beforeend", `
<div class="slide">
<figure>
<img src="${slide.img}" width="480">
<figcaption class="cap">${slide.cap}</figcaption>
</figure>
</div>`);
});
const slides = Array.from(frame.querySelectorAll(".slide"));
slides[0].classList.add("active");
return slides;
}
.box {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 96vw;
min-height: 96vh;
border: 3px dashed #000;
}
.frame {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
/* The element that is the parent of all .slide should be relative so the
slides, which are absolute positioned, can sit within .frame's perimeter */
position: relative;
}
.slide {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
/* All slides should be out of normal flow */
position: absolute;
/* All slides should be in the layer "under" the visible layer (z-index: 0+) */
z-index: -1;
opacity: 0;
animation: opacity 0.7s ease-in;
}
.active {
opacity: 1;
z-index: 1;
}
figure {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
max-height: 100%;
margin: 0;
padding: 0;
border-style: 1px dashed #000;
}
img {
object-fit: contain;
}
.cap {
min-width: 100%;
text-align: center;
white-space: pre;
}
button {
display: inline-flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
z-index: 2;
padding: 0 0.5rem;
border: none;
border-radius: 0.25rem;
outline: none;
font-size: 4rem;
color: rgba(255, 255, 255, 0.5);
background: none;
background-color: rgba(0, 0, 0, 0.1);
transform: translateY(-50%);
transition: 0.5s;
cursor: pointer;
}
button:hover,
button:focus {
color: #fff;
background-color: rgba(0, 0, 0, 0.3);
}
button:active {
color: rgba(0, 0, 0, 0.5);
background: none;
}
.prev {
left: 1rem;
}
.next {
right: 1rem;
}
<main>
<h2>Content Title</h2>
<article class="box">
<button class="prev">❮</button>
<button class="next">❯</button>
<section class="frame"></section>
</article>
</main>
When I start to scroll down, the section home moves down and in between is a weird space which I don't know how to get rid of.
Any tips on how to fix this problem?
//header Effekt beim scrollen
$(function() {
var shrinkHeader = 100;
$(window).scroll(function() {
var scroll = getCurrentScroll();
if (scroll >= shrinkHeader) {
$('#navbar').addClass('shrink');
} else {
$('#navbar').removeClass('shrink');
}
});
function getCurrentScroll() {
return window.pageYOffset || document.documentElement.scrollTop;
}
});
// JavaScript Document
$(document).ready(function() {
var navTop = $('#navbar').offset().top;
var navHeight = $('#navbar').height();
var windowH = $(window).height();
$('.section').height(windowH);
$(document).scroll(function() {
var st = $(this).scrollTop();
//for the nav bar:
if (st > navTop) {
$('#navbar').addClass('fix');
$('.section:eq(0)').css({
'margin-top': navHeight
}); //fix scrolling issue due to the fix nav bar
} else {
$('#navbar').removeClass('fix');
$('.section:eq(0)').css({
'margin-top': '0'
});
}
$('.section').each(function(index, element) {
if (st + navHeight > $(this).offset().top && st + navHeight <= $(this).offset().top + $(this).height()) {
$(this).addClass('active');
var id = $(this).attr('id');
$('a[href="#' + id + '"]').parent('li').addClass('active');
// or $('#nav li:eq('+index+')').addClass('active');
} else {
$(this).removeClass('active');
var id = $(this).attr('id');
$('a[href="#' + id + '"]').parent('li').removeClass('active');
//or $('#nav li:eq('+index+')').removeClass('active');
}
});
});
});
//
#charset "utf-8";
/* CSS Document */
* {
font-family: 'Roboto', sans-serif;
}
#container {
background-color: white;
width: 1280px;
height: 4000px;
margin-left: auto;
margin-right: auto;
}
body {
background-color: grey;
margin: 0px;
}
/* Navigation */
ul {
color: black;
list-style: none;
float: right;
margin-right: 20px;
padding-top: 32px;
}
ul li {
display: inline-table;
margin-left: 5px;
padding: 5px;
border-bottom: 1.5px solid;
border-bottom-color: white;
}
ul li a {
color: black;
text-decoration: none;
padding: 10px;
}
/* Smart Navbar / weiß wo man auf der Seite ist von https://stackoverflow.com/questions/19697696/change-underline-of-active-nav-by-section */
#navbar.fix {
position: fixed;
top: 0;
}
#navbar li.active {
border-bottom: 1.5px solid;
border-bottom-color: #f6bd60;
}
/* Smart Navbar Ende */
/* fixed Navigation von https://codepen.io/malZiiirA/pen/cbfED?css-preprocessor=none */
#navbar {
border-bottom-style: solid;
border-bottom-width: 1.25px;
box-shadow: 0px 2.5px 5px rgba(0, 0, 0, 0.2);
background-color: white;
height: 128px;
transition: 0.32s;
position: fixed;
width: 1280px;
}
#navbar.shrink {
height: 80px;
line-height: 18px;
}
#navbar li {
font-size: 18px;
font-weight: normal;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
transition: all 0.3s;
}
#navbar.shrink li {
font-size: 18px;
margin-top: -30px;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
transition: all 0.3s;
}
/* fixed nav Ende */
#spacer {
height: 128px;
border-bottom: 4px solid;
}
#Home {
height: 1000px;
border-style: solid;
}
#UberUns {
height: 1000px;
border-style: solid;
}
#Aktionen {
height: 1000px;
border-style: solid;
}
#Terminvereinbarung {
height: 1000px;
border-style: solid;
}
#Infos {
height: 1000px;
border-style: solid;
}
/* Hover Effekt bei Navigation von https://github.com/IanLunn/Hover/blob/master/css/hover.css */
.hvr-sweep-to-top {
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
position: relative;
-webkit-transition-property: color;
transition-property: color;
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
}
.hvr-sweep-to-top:before {
content: "";
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #f6bd60;
-webkit-transform: scaleY(0);
transform: scaleY(0);
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
-webkit-transition-property: transform;
transition-property: transform;
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-timing-function: ease-out;
transition-timing-function: ease-out;
}
.hvr-sweep-to-top:hover,
.hvr-sweep-to-top:focus,
.hvr-sweep-to-top:active {
color: white;
}
.hvr-sweep-to-top:hover:before,
.hvr-sweep-to-top:focus:before,
.hvr-sweep-to-top:active:before {
-webkit-transform: scaleY(1);
transform: scaleY(1);
}
/* Hover Effekt Ende */
/* Loader */
.loader {
display: inline-block;
width: 30px;
height: 30px;
position: relative;
border: 4px solid #Fff;
animation: loader 2s infinite ease;
}
.loader-inner {
vertical-align: top;
display: inline-block;
width: 100%;
background-color: #fff;
animation: loader-inner 2s infinite ease-in;
}
#keyframes loader {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(180deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(360deg);
}
100% {
transform: rotate(360deg);
}
}
#keyframes loader-inner {
0% {
height: 0%;
}
25% {
height: 0%;
}
50% {
height: 100%;
}
75% {
height: 100%;
}
100% {
height: 0%;
}
}
.loader-wrapper {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: #242f3f;
display: flex;
align-items: center;
justify-content: center;
}
/* loader Ende */
<!DOCTYPE html>
<html>
<head>
<title>OptikTack</title>
<link href="style.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
</head>
<body>
<div id="container">
<div class="body">
<div id="navbar">
<script class="cssdeck" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="javascript/navbar fixed.js"></script>
<ul>
<li class="hvr-sweep-to-top">Home</li>
<li class="hvr-sweep-to-top">Wir über uns</li>
<li class="hvr-sweep-to-top">Aktionen</li>
<li class="hvr-sweep-to-top">Terminvereinbarung</li>
<li class="hvr-sweep-to-top">Infos</li>
</ul>
</div>
<div id="spacer"></div>
<section id="Home" class="section">
<p>Home</p>
</section>
<section id="UberUns" class="section">
<p>section 2</p>
</section>
<section id="Aktionen" class="section">
<p>section 3</p>
</section>
<section id="Terminvereinbarung" class="section">
<p>section 4</p>
</section>
<section id="Infos" class="section">
<p>section 5</p>
</section>
</div>
</div>
<div class="loader-wrapper">
<span class="loader"><span class="loader-inner"></span></span>
</div>
<script>
$(window).on("load", function() {
$(".loader-wrapper").fadeOut("slow");
});
</script>
<script>
$('a').click(function() {
$('html, body').animate({
scrollTop: $($(this).attr('href')).offset().top
}, 500);
return false;
});
</script>
</body>
</html>
Something is creating a margin-top when you scroll down
try add:
#Home {
margin-top: 0 !important;
}
Looking at your code I found a couple of issues.
if(st > navTop ){
$('#navbar').addClass('fix');
$('.section:eq(0)').css({'margin-top':navHeight});//fix scrolling issue due to the fix nav bar
}
st is defined to be the current scrolling position of your browser.
This is in your scrolling code, meaning as soon as st is bigger than navTop, it will set margin-top to navHeight. But the issue is that a little bit up you defined navTop as followed: var navTop = $('#navbar').offset().top;
navTop is always 0. Meaning that whenever you scroll any amount it will add the margin-top. I'd start there with finding your problem.
Andrea's solution is but a solution for a problem that you introduced yourself.
try add:
#Home {
margin-top: 0 !important;
}
There are similar questions like this and this, but don't address this situation.
The goal is to slide a menu onto the screen with CSS translation when its parent is shown. However, showing the parent then applying the CSS class to trigger the translation happens instantly instead of over time. Effectively, there's no animation.
JavaScript could be used to slide the child element onto the screen, but the goal is to keep as much of the animation logic in CSS.
Setting the opacity to 0 doesn't work because we need the menu and its parent to not take any space or be part of the layout.
Codepen: https://codepen.io/Crashalot/pen/YzXmjYj
function toggleSidebar() {
$("#sidebar").toggleClass("show");
}
$("#button").on("click", function() {
toggleSidebar();
});
body {
margin: 0;
padding: 0;
}
#button {
position: fixed;
top: 0;
left: 200px;
width: 150px;
height: 50px;
background: yellow;
display: flex;
justify-content: center;
align-items: center;
z-index: 8;
cursor: pointer;
}
#sidebar {
display: none;
}
#sidebar.show {
display: block;
}
.overlay {
position: fixed;
width: 100vw;
height: 100vh;
background: red;
z-index: -1;
}
.menuBox {
width: 200px;
height: 100vh;
background: blue;
transition: 0.3s ease-in-out;
transform: translate(-100%);
}
#sidebar.show .menuBox {
transform: translate(0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sidebar">
<div class="overlay"></div>
<div class="menuBox"></div>
</div>
<div id="button">CLICK ME</div>
You can't animate display: none; Set opacity to 0 and then 1 on toggle class. Here's the CodePen for you. ;)
I added a button for the toggle event. Let me know if you need any more help!
enter link description here
$(".btn").on("click", toggleSidebar);
function toggleSidebar() {
$("#sidebar").toggleClass("show");
}
body {
margin: 0;
padding: 0;
}
#sidebar {
opacity: 0;
transition: all 300ms ease-in-out;
}
#sidebar.show {
display: block;
opacity: 1;
}
.overlay {
position: fixed;
width: 100vw;
height: 100vh;
background: red;
z-index: -1;
}
.menuBox {
width: 200px;
height: 100vh;
background: blue;
transition: 300ms ease-in-out;
-webkit-transform: translate(-100%);
}
#sidebar.show .menuBox {
-webkit-transform: translate(0);
}
.btn {
position: absolute;
top: 10px;
left: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sidebar">
<div class="overlay"></div>
<div class="menuBox"></div>
</div>
<button class="btn">Click</button>
You need to consider an animation. The animation will run automatically when the element appear on the screen
function toggleSidebar() {
$("#sidebar").toggleClass("show");
}
$("#button").on("click", function() {
toggleSidebar();
});
body {
margin: 0;
padding: 0;
}
#button {
position: fixed;
top: 0;
left: 200px;
width: 150px;
height: 50px;
background: yellow;
display: flex;
justify-content: center;
align-items: center;
z-index: 8;
cursor: pointer;
}
#sidebar {
display: none;
}
#sidebar.show {
display: block;
}
.overlay {
position: fixed;
width: 100vw;
height: 100vh;
background: red;
z-index: -1;
}
.menuBox {
width: 200px;
height: 100vh;
background: blue;
transform: translate(-100%);
animation: show 0.3s ease-in-out forwards;
}
#keyframes show {
to {
transform: translate(0);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sidebar">
<div class="overlay"></div>
<div class="menuBox"></div>
</div>
<div id="button">CLICK ME</div>
updating display to a value other than none will start all animations applied to the element by the animation-name property, as well as all animations applied to descendants with display other than none. ref
I think you should define the action for your function called. When load page or on click like below:
$(document).ready(function () {
$('#sidebar').on('click', function () {
$(this).toggleClass('show');
});
});
I am trying to implement a notification system in my app without the use of a library. Here is a gif of the issue: https://imgur.com/oRc11dM
And here is the jsfiddle of the gif: https://jsfiddle.net/w9yk7n54/
When you click on new notification button, already displayed notifications jump up to make room for the new notification and new notification slides in. I was wondering how I could make it so that they all smoothly go up together.
The notifications wont all be the same dimensions so I cant set static values for height/etc.
Thank you!
let btn = document.querySelector('button')
let container = document.querySelector('.notifications-container')
let notif_contents = [
"<p>1</p><p>1</p><p>1</p><p>1</p>",
"<p>test</p>",
"<div><h1>testtesttest</h1><p>yoloalsdfasdf</p></div>"
]
let current = 0
btn.onclick = () => {
let notif = document.createElement('div')
notif.classList.add('notif')
notif.innerHTML = notif_contents[current]
notif.addEventListener('animationend', () => {
notif.parentElement.removeChild(notif)
})
current++
container.append(notif)
}
* {
box-sizing: border-box;
}
.container {
height: 300px;
width: 400px;
border: 1px solid black;
position: absolute;
}
.notifications-container {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 200px;
background: rgba( 0, 0, 0, 0.2);
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow: hidden;
}
.notif {
position: relative;
width: 100%;
padding: 10px;
border: 1px solid black;
animation: notifAnim 5s forwards;
transition: all .2s;
background: white;
}
button {
z-index: 100;
background: lightcoral;
color: white;
border: none;
padding: 10px;
font-size: 20px;
margin: 5px;
}
#keyframes notifAnim {
0% {
transform: translateY( 100%)
}
20% {
transform: translateY( 0)
}
80% {
transform: translateY( 0)
}
100% {
transform: translateY( 100%)
}
}
<div class="container">
<button>New Notification</button>
<div class="notifications-container"></div>
</div>
Your notif container has justify-content: flex-end. This means that whenever you add a new one, the previous ones will be pushed up with the height of the new one.
The "fix" is to give each element a negative margin-top equal to its height and integrate in your current transition getting margin-top back to 0.
Example:
let btn = document.querySelector('button'),
container = document.querySelector('.notifications-container'),
notif_contents = [
"<p>1</p><p>1</p><p>1</p><p>1</p>",
"<p>test</p>",
"<div><h1>testtesttest</h1><p>yoloalsdfasdf</p></div>",
"<code>another.test()</code>",
"<strong>another</strong> <i>test</i>"
]
btn.onclick = () => {
let notif = document.createElement('div'),
index = Math.floor(Math.random() * notif_contents.length)
notif.classList.add('notif')
notif.innerHTML = notif_contents[index]
notif.addEventListener('animationend', () => {
notif.parentElement.removeChild(notif)
})
container.append(notif)
notif.style.marginTop = '-' + notif.offsetHeight + 'px'
}
* {
box-sizing: border-box;
}
.container {
height: 300px;
width: 400px;
border: 1px solid #888;
position: absolute;
}
.notifications-container {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 200px;
background: rgba( 0, 0, 0, 0.2);
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow: hidden;
}
.notif {
position: relative;
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-bottom: none;
animation: notifAnim 5s forwards;
background: white;
}
button {
z-index: 100;
background: lightcoral;
color: white;
border: none;
padding: 10px;
font-size: 20px;
margin: 5px;
}
#keyframes notifAnim {
0%,
100% {
transform: translateY( 100%)
}
20%,
80% {
transform: translateY( 0);
margin-top: 0
}
}
<div class="container">
<button>New Notification</button>
<div class="notifications-container"></div>
</div>
An idea is to insert new element with a height equal to 0 and you animate the height in addition to translate. Of course, you should use max-height since height are unknown and we cannot animate to auto:
let btn = document.querySelector('button')
let container = document.querySelector('.notifications-container')
let notif_contents = [
"<p>1</p><p>1</p><p>1</p><p>1</p>",
"<p>test</p>",
"<div><h1>testtesttest</h1><p>yoloalsdfasdf</p></div>"
]
let current = 0
btn.onclick = () => {
let notif = document.createElement('div')
notif.classList.add('notif')
notif.innerHTML = notif_contents[current]
notif.addEventListener('animationend', () => {
notif.parentElement.removeChild(notif)
})
current++
container.append(notif)
}
* {
box-sizing: border-box;
}
.container {
height: 300px;
width: 400px;
border: 1px solid black;
position: absolute;
}
.notifications-container {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 200px;
background: rgba( 0, 0, 0, 0.2);
display: flex;
flex-flow: column nowrap;
justify-content: flex-end;
overflow: hidden;
}
.notif {
position: relative;
width: 100%;
padding:0 10px;
max-height:0px;
border: 1px solid black;
animation: notifAnim 5s forwards;
transition: all .2s;
background: white;
}
button {
z-index: 100;
background: lightcoral;
color: white;
border: none;
padding: 10px;
font-size: 20px;
margin: 5px;
}
#keyframes notifAnim {
0% {
transform: translateY( 100%);
max-height:0;
padding:0 10px;
}
30% {
transform: translateY( 0);
max-height:300px;
padding:10px;
}
80% {
transform: translateY( 0);
max-height:300px;
padding:10px;
}
100% {
transform: translateY( 100%);
max-height:300px;
padding:10px;
}
}
<div class="container">
<button>New Notification</button>
<div class="notifications-container"></div>
</div>
I want to implement ability to collapse the gray .content-wrapper block (with blue sqare inside) smoothly based on drag value when user drags .drag element. What is the best and efficient way nowadays to do this for mobile touch drag?
* {
box-sizing: border-box;
color: white;
}
.drag {
width: 50px;
height: 3px;
border-radius: 3px;
opacity: 0.3;
margin: 0 auto;
background: linear-gradient(to left, white, rgba(255, 255, 255, 0.8) 50%, white 100%);
transition: opacity 0.4s;
}
.screen {
padding-top: 40px;
margin: 0 auto;
border-radius: 5px;
overflow: hidden;
background: #2b2d5b;
width: 320px;
height: 568px;
}
.controls {
background: linear-gradient(to bottom, #fd2929, #fd5c5c);
height: 100px;
}
.drag-wrapper {
display: block;
padding: 20px;
}
.drag-wrapper:active .drag,
.drag-wrapper:hover .drag,
.drag-wrapper:focus .drag {
opacity: 0.7;
}
.board {
padding: 0 20px;
}
.content-wrapper {
padding: 20px;
height: 320px;
}
.content-wrapper {
background: #444;
}
.content {
display: flex;
justify-content: center;
align-items: center;
font-family: Helvetica;
background: linear-gradient(to bottom, #3adffd, #00abfb);
height: 100%;
}
<div class="screen">
<div class="content-wrapper">
<div class="content">
.content
</div>
</div>
<a href="javascript:void(0)" class="drag-wrapper">
<div class="drag"></div>
</a>
<div class="board">
<div class="controls"></div>
</div>
</div>
here's my implementation for this case with hammer.js
http://hammerjs.github.io
movement of the board could have more complex calculations based on e.velocityY, but I'm using quick CSS transition when pan ended which looks good for my case
$(document).ready(function() {
let sliderManager = new Hammer.Manager(document.querySelector('.drag-wrapper'));
let board = document.querySelector('.board');
let threshold = 150;
let boardTopInitial = board.style.top = board.offsetTop;
let boardTopCollaped = 30;
sliderManager.add(new Hammer.Pan({
threshold: 0,
pointers: 0
}));
sliderManager.on('pan', function(e) {
e.preventDefault();
board.classList.remove("transitable");
board.classList.add("full-height");
if (!board.classList.contains('pinned-top') && e.deltaY < 0) {
board.style.top = boardTopInitial + e.deltaY + "px";
if (e.isFinal) {
board.classList.add("transitable");
if (Math.abs(e.deltaY) > threshold) {
board.style.top = boardTopCollaped + "px";
board.classList.add("pinned-top");
} else {
board.setAttribute('style', '');
board.classList.remove("full-height");
}
}
} else if (board.classList.contains('pinned-top') && e.deltaY > 0) {
board.style.top = boardTopCollaped + e.deltaY + "px";
if (e.isFinal) {
board.classList.add("transitable");
if (Math.abs(e.deltaY) > threshold) {
board.setAttribute('style', '');
board.classList.remove("pinned-top");
board.classList.remove("full-height");
} else {
board.style.top = boardTopCollaped + "px";
board.classList.add("top");
}
}
}
})
})
* {
box-sizing: border-box;
color: white;
}
.drag {
width: 50px;
height: 3px;
border-radius: 3px;
opacity: 0.3;
margin: 0 auto;
background: linear-gradient(to left, white, rgba(255, 255, 255, 0.8) 50%, white 100%);
transition: opacity 0.4s;
}
.pinned-top {
top: 30px;
}
.full-height {
box-shadow: none;
min-height: 100vh;
}
.transitable {
transition: top .2s ease-out;
}
.screen {
position: relative;
padding-top: 40px;
margin: 0 auto;
border-radius: 5px;
overflow: hidden;
background: #2b2d5b;
width: 320px;
height: 568px;
}
.controls {
background: linear-gradient(to bottom, #fd2929, #fd5c5c);
height: 100px;
}
.drag-wrapper {
display: block;
padding: 20px;
}
.drag-wrapper:active .drag,
.drag-wrapper:hover .drag,
.drag-wrapper:focus .drag {
opacity: 0.7;
}
.board {
padding: 0 20px;
position: absolute;
background: #2b2d5b;
top: 360px;
left: 0;
right: 0;
}
.content-wrapper {
padding: 20px;
height: 320px;
}
.content-wrapper {
background: #444;
}
.content {
display: flex;
justify-content: center;
align-items: center;
font-family: Helvetica;
background: linear-gradient(to bottom, #3adffd, #00abfb);
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://hammerjs.github.io/dist/hammer.min.js"></script>
<div class="screen">
<div class="content-wrapper">
<div class="content">
.content
</div>
</div>
<div class="board">
<a href="javascript:void(0)" class="drag-wrapper">
<div class="drag"></div>
</a>
<div class="controls"></div>
</div>
</div>