I am adding a class to a div after a user scrolls. This works fine, but for some reason it won't remove this class when the user scrolls back again. There are no errors in the console. Where am I going wrong?
var scrolled = $('body').offset().top - 800;
$(window).on('resize scroll', function() {
if ($(window).scrollTop() > scrolled) {
$('#one').addClass('in');
} else {
$('#one').removeClass('in');
}
});
section.bg-red {
background: red;
}
section.bg-blue {
background: blue;
}
section {
min-height: 100vh;
}
section p {
color: red;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
section.in p {
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="bg-red" id="one">
<p>Well done you scrolled</p>
</section>
<section class="bg-blue">
ddd
</section>
View on Codepen
The problem is that you are subtracting 800 from the body's offset top, which will produce a negative number. The window's scroll top will never be a negative number, so the class will never be removed.
section.bg-red {
background: red;
}
section.bg-blue {
background: blue;
}
section {
min-height: 100vh;
}
section p {
color: red;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
section.in p {
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="bg-red" id="one">
<p>Well done you scrolled</p>
</section>
<section class="bg-blue">
ddd
</section>
<script>
var scrolled = $('body').offset().top;
$(window).on('resize scroll', function() {
if ($(window).scrollTop() > scrolled) {
$('#one').addClass('in');
} else {
$('#one').removeClass('in');
}
});
</script>
I found a solution using Waypoints.js which does as required. All that is necessary is to include waypoints.js to the project and write the following Javascript below.
var $elone = $('.element-one');
$elone.waypoint(function(direction) {
if (direction == 'down') {
$elone.addClass('in');
}
else {
$elone.removeClass('in');
}
}, {offset: '50%'});
This allows you to use percentages instead of pixels which works better for a responsive website.
Related
I have a mobile nav and I open it using this code in JS file:
var toggleButton = document.querySelector(".toggle-button");
var mobileNav = document.querySelector(".mobile-nav");
var closeButton = document.querySelector(".close-button");
toggleButton.addEventListener("click", function () {
mobileNav.classList.add("slide");
});
closeButton.addEventListener("click", function () {
mobileNav.classList.remove("slide");
});
.mobile-nav {
position: fixed;
transition: 0.4s ease;
transform: translateX(1400px);
}
.slide {
transform: translateX(0);
}
when I scroll down and open mobile nav using slide class the scroll bar goes up, how to fix it? i want it to remain at current place where I open slide menu.
here is project Github page for more codes:
enter link description here
and here is the online website for reprodure the issue, just make browser page smaller than 768px and scroll down then tap hamburger icon to see the issue.
enter link description here
const navSection = document.querySelector(".nav-section");
function openNav() {
navSection.classList.add("open-nav")
}
function closeNav() {
navSection.classList.remove("open-nav")
}
.nav-section{
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: 0.25s;
pointer-events: none;
}
.nav-section nav{
display: flex;
flex-direction: column;
position: relative;
transform: translateX(-100%);
width: 20rem;
height: 100%;
background: red;
}
.open-nav{
pointer-events: all;
opacity: 1;
background: rgba(0, 0, 0, 0.5);
}
.open-nav nav{
transform: translateX(0%);
}
.close-nav-btn{
position: absolute;
right: 0;
top: 0;
}
<div class="open-btn">
<button onclick="openNav()">open</button>
</div>
<section class="nav-section">
<nav>
<a>nav 0</a>
<a>nav 1</a>
<a>nav 2</a>
<a>nav 3</a>
</nav>
<button onclick="closeNav()" class="close-nav-btn">close</button>
</section>
Like this maybe?
I am working on a little menu animation, nothing groundbreaking but just as an experiment. This is what I currently have:
HTML
<div class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
SCSS
div.menu {
width: 24px;
height: 24px;
position: relative;
margin: 48px;
cursor: pointer;
div.bar {
display: block;
width: 100%;
height: 2px;
background-color: #444;
position: absolute;
transition: all 0.25s ease-in-out;
&:nth-child(1) {
}
&:nth-child(2) {
top: 11px;
}
&:nth-child(3) {
top: 11px;
}
&:nth-child(4) {
bottom: 0;
}
}
&.active {
div.bar {
&:nth-child(1) {
width: 0;
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
width: 0;
}
}
}
}
JAVASCRIPT
var menu = document.querySelector('.menu');
menu.addEventListener('click', function(){
menu.classList.toggle('active');
});
And this is a pen of it in action:
https://codepen.io/mikehdesign/pen/eWJKKN
Currently, when the menu is active the top and bottom div.bar reduce their width to 0 to the left. I would like to adjust this so they reduce their width to the center. I have tried messing with margins for them but had no luck, if anyone could shed some light or suggest a different approach if needed that would be great.
Mike
You can use transform-origin to do this:
with respect to your dimensions it would be 12px up and down(+ and -) as in the code below:
&.active {
div.bar {
&:nth-child(1) {
transform-origin:12px 12px;
transform: scale(0);
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
transform-origin:12px -12px;
transform: scale(0);
}
}
}
Check this out: JsFiddle Link
I would use the pseudo elements to split up the transform animation.
Demo (no script version)
HTML (note only three bars)
<input type="checkbox" id="toggle-menu" hidden>
<label for="toggle-menu" class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</label>
SCSS
// two step transition
// as translate and rotation can't (yet) be animated individually
// we use the pseudo elements to split up the animation
//
// - bar elements will handle the vertical transform
// - :after elements will handle rotation/scale
// variables to control transition time and delay
$transition-time: 300ms;
$transition-delay: 300ms;
.menu {
width: 24px;
height: 24px;
position: relative;
cursor: pointer;
display: block;
}
.bar {
height: 2px;
position: absolute;
top: 50%;
width:100%;
// Entering `hamburger` state
// 1) add a delay on bars to wait for the :after elements to rotate/scale back
// 2) the :after elements have no delay
transition: $transition-time $transition-delay; // 1
&:after {
content:'';
display:table;
background: black;
position: inherit; width: inherit; height:inherit;
transition: $transition-time; // 2
}
// transform the bars into hamburger
&:nth-child(1){ transform: translateY(-8px); }
&:nth-child(3){ transform: translateY(8px);}
}
// when toggle-menu checkbox is checked transform to `X`
[id="toggle-menu"]:checked ~ .menu {
.bar {
// Entering `X` state
// 1) to animate bars to the center we simply remove the transform
// 2) as we are now animating backwards we switch the transition
// on the bars and their :after elements
transform: none; // 1
transition: $transition-time; // 2
&:after { transition: $transition-time $transition-delay } // 2
// rotate the top and bottom :after elements
&:nth-child(1):after{ transform: rotate(-45deg); }
&:nth-child(3):after{ transform: rotate(45deg);}
// hide the middle :after by scaling to zero
// (when all bars are at the center)
&:nth-child(2):after{ transform: scale(0); }
}
}
So I've managed to create a rotation that only rotates in one direction by using a setTimeout. I was wondering if I could still achieve this without using it. The main issue I have is that if I click fast enough, it will still spin the other way.
So overall, is there a way to make it spin in one direction regardless of how fast I click.
function clicked()
{
element = $("#spin");
if ($(element).hasClass("rotate2"))
{
$(element).removeClass("rotate rotate2");
setTimeout( () => $(element).addClass("rotate"), 1);
}
else if($(element).hasClass("rotate"))
{
$(element).addClass("rotate2");
}
else
{
$(element).addClass("rotate");
}
}
div.horizontal {
position: absolute;
width: 100%;
height: 5px;
background-color: red;
top: 50%;
transform: translateY(-50%);
}
div.horizontal.rotate {
transform: translateY(-50%) rotate(-180deg);
transition: transform 0.5s;
}
div.horizontal.rotate2 {
transform: translateY(-50%) rotate(-360deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width: 50px; height: 50px; position: relative;">
<div id="spin" class="horizontal">
</div>
</div>
<button onclick="clicked()">Rotate</button>
You can just use a single class for the transition, and remove the class on transitionend
var $button = $('#button'),
$spin = $('#spin');
$(document).on('click', $button, function() {
$spin.addClass('rotate').on('transitionend',function() {
$(this).removeClass('rotate');
});
})
div.horizontal {
position: absolute;
width: 100%;
height: 5px;
background-color: red;
top: 50%;
}
div.horizontal.rotate {
transform: rotate(-180deg);
transition: transform .5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width: 50px; height: 50px; position: relative;">
<div id="spin" class="horizontal">
</div>
</div>
<button id="button">Rotate</button>
I have a keyframe animation which plays when I hover on element. After the mouseout event, it stops too abruptly. How could I force it play till it's end? I tried on.(animationend) event, it doesn't work. Transform origin and huge delay, either don't work. Thanks.
CodePen Demo
class Main {
constructor() {
}
waveOn() {
$(this).addClass('wave-active');
}
waveOut() {
var elem = $('.info__block');
elem.removeClass('wave-active');
}
jsInit() {
$('.info__block').hover(this.waveOn);
$('.info__block').on('animationend', this.waveOut)
}
}
new Main().jsInit();
.info__block {
width: 100px;
height: 100px;
background: aqua;
border-radius: 50px;
position: relative;
display: flex;
justify-content: center;
margin-top: 100px;
}
.info__block:before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50px;
border-top: 2px solid aqua;
}
.info__block.wave-active:before {
animation: link-line 2.5s infinite .5s linear;
}
#keyframes link-line {
0% {
transform: translateY(0);
opacity: 1;
}
60% {
opacity: 0;
}
100% {
transform: translateY(-50%) scale(1.6);
opacity: 0;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="info__block info__block-1">
</div>
Here is a solution to your problem using the native animationiteration event that is described in the W3C Spec for animations. This event is fired after every single iteration of the animation. So, what we are doing is that on hover out, we are attaching the animationiteration event listener (which will get fired only once due to the one). Within this event's listener, I've simply placed the contents of original waveOut function. So, everytime you hover the mouse out of the element, the animation will complete one single iteration (after the hover out has happened) and then stop with that. I think this is a lot more graceful than an abrupt end.
class Main {
constructor() {}
jsInit() {
$('.info__block').hover(function() {
$('.info__block').off('animationiteration'); /* switch off the event handler when you quickly hover back in again */
$('.info__block').addClass('wave-active');
}, function() {
$('.info__block').one('animationiteration', function() {
$('.info__block').removeClass('wave-active');
})
});
}
}
new Main().jsInit();
body {
padding: 200px 200px;
}
.info__block {
width: 100px;
height: 100px;
background: aqua;
border-radius: 50px;
position: relative;
display: flex;
justify-content: center;
}
.info__block:before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50px;
border-top: 2px solid aqua;
}
.info__block.wave-active:before {
animation: link-line 2.5s infinite .5s linear;
}
#keyframes link-line {
0% {
transform: translateY(0);
opacity: 1;
}
60% {
opacity: 0;
}
100% {
transform: translateY(-50%) scale(1.6);
opacity: 0;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="info__block info__block-1">
</div>
(Note: In the above demo sometimes the animation stops after just one iteration during the second and subsequent hover operations. This seems to be some glitch with the Run Snippet window. I don't see this problem happening in the Editor's output window or in this CodePen demo. If you also encounter the same problem let me know and I'll see if there is any fix for it.)
Note: The problem mentioned above has been fixed and the snippet is also updated with the revised code. Revised CodePen Demo.
An infinite animation doesn't have animationend event.
I added this pure css cookie bar to my website and all works fine, the only problem is that when you enter in the site, you can see FIRST the cookie bar, AND the cookie bar go up and go down at the end.
How can see my cookie bar only go down when i enter in my site, i thought to change de thenimation delay, add set time out .... but nothing change !!
here is the original codepen and you can see what i want to change in it
www.codepen.io/natewiley/pen/uGtcD
HERE IS MY CODE
<input class="checkbox-cb" id="checkbox-cb" type="checkbox" />
<div class="cookie-bar">
<div class="message">
This website uses cookies to give you an incredible experience. By using
this website you agree to the
<div class="buttoncookies-container">
<a style="letter-spacing: 1px;" class="buttoncookies" id="modalcookieslinken" onclick="toggleOverlay()">terms</a>
</div>
</div>
<div class="mobile">
This website uses cookies,
<div class="buttoncookies-container">
<a style="letter-spacing: 1px;" class="buttoncookies" id="modalcookiesshortlink" onclick="toggleOverlay()">
learn more
</a>
</div>
</div>
<label for="checkbox-cb" class="close-cb">X</label>
</div>
</div>
HERE IS MY CSS
.cookie-bar { z-index:9996; position: fixed; width: 100%; top: 0; right: 0; left: 0; height: auto; padding: 20px; line-height:20px; text-align: center; background: #d2c6af; transition: .8s; animation: slideIn .8s; animation-delay: .8s; display: inline-block; }
.mobile { display: none; }
#keyframes slideIn { 0% { transform: translateY(-1000px); } 100% { transform: translateY(0); } }
.close-cb { border: none; background: none; position: absolute; display: inline-block; right: 20px; top: 10px; cursor: pointer; }
.close-cb:hover { color:#fff;; }
.checkbox-cb { display: none;}
#checkbox-cb:checked + .cookie-bar { transform: translateY(-1000px); }
Removing the line in css
animation-delay: .8s;
will give you the result
Make the animation last longer.
animation: slideIn 4s;
Plus add some trick to animation flow:
0% {
transform: translateY(-50px);
}
50% {
transform: translateY(-50px);
}
100% {
transform: translateY(0);
}